2012-04-19 24 views
5

Hay un tipo de zócalo sin formato en Network.Socket, pero cerca de los zócalos de enlace hay un comentario "Actualmente solo son compatibles los zócalos de dominio Unix y las familias de Internet". ¿Cómo puedo usar enchufes sin procesar en haskell?Enchufes sin procesar en Haskell

Lo que estoy tratando de lograr, como el trabajo código Python:

import binascii 
import socket 

# Create raw socket. 
ethType = b'FFFF' # 2 bytes, has to be >= 0x0600. FFFF is "unavailable" by IEEE. 
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) 
sock.bind(('lo', int(ethType,16))) 

# Create packet. 
srcMac = b'000000000000' # 6 bytes 
destMac = b'000000000000' # 6 bytes 
header = binascii.a2b_hex(destMac + srcMac + ethType) # 14 bytes 
message = b'Hello, World!' 
sock.send(header + message) 

# Receive such packets 
while True: print (sock.recv(65535)) 

EDIT1: En Haskell, utilizo sock <- socket AF_PACKET Raw 0xFFFF para crear un socket, pero bindSocket requiere un SockAddr como argumento, para lo cual dispone de constructores son

SockAddrInet PortNumber HostAddress 
SockAddrInet6 PortNumber FlowInfo HostAddress6 ScopeID 
SockAddrUnix String 

pero ninguno de estos parece correcto.

Edit2: Gracias a un comentario de Yuras me reciben paquetes a trabajar:

import Network.Socket 
sock <- socket AF_PACKET Raw 0xFFFF 
recv sock 0xFFFF 

Edit3: intentar enviar un paquete desde un enchufe sin atar el resultado es una excepción:

sock <- socket AF_PACKET Raw 0xFFFF 
send sock "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\255\255" 
*** Exception: send: does not exist (No such device or address) 

Esto tiene sentido, porque el kernel no tendría ninguna pista sobre qué interfaz transmitir realmente el paquete. ¿Hay alguna manera de vincular un socket (sin formato) a una interfaz en Haskell?

+1

... ¿cuál es su pregunta? –

+1

['AF_PACKET'] (http://hackage.haskell.org/packages/archive/network/2.3.0.11/doc/html/src/Network-Socket-Internal.html#Family)' es compatible, como es [ 'SOCK_RAW'] (http://hackage.haskell.org/packages/archive/network/2.3.0.11/doc/html/src/Network-Socket.html#SocketType). Parece una traducción directa para mí. –

+0

¿Cómo hacer esto en Haskell? – Andres

Respuesta

4

Network.Pcap se puede utilizar para enviar y recibir datos en bruto.

import qualified Data.ByteString.Char8 as B 
import Network.Pcap 

main = do 
    -- open device 
    dev <- openLive "lo" 65535 False 0 
    setFilter dev "ether proto 0xFFFF" True 0 

    -- send 
    sendPacketBS dev (B.pack "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\255\255Hello, World!") 

    -- receive 
    loopBS dev (-1) (\_ packet -> putStrLn (show packet)) 
1

Esta interfaz es compatible con Network.Socket.

+0

En realidad, no lo es. Creo que 'Network.Socket' no sabe nada sobre' struct sockaddr_ll' que se requiere para vincular los zócalos 'AF_PACKET'. Solo se admiten las direcciones de los sabores Inet, Inet6 y Unix. – drdaeman

1

En primer lugar, no necesita vincular el socket sin formato para usarlo. De man 7 raw

Un socket sin formato puede vincularse a una dirección local específica mediante la llamada bind (2). Si no está vinculado, se reciben todos los paquetes con el protocolo IP especificado.

Si desea ligarlo, a continuación, puede utilizar bindSocket porque los conectores directos usan la misma estructura sockaddr como ip utiliza:

conectores directos usan la estructura de dirección sockaddr_in estándar definido en ip (7)

+0

¿Por qué es así que puedo recibir sin atar el zócalo, pero no puedo enviar (intenté con el código de Python anterior, obtuve '[Errno 6] Sin dicho dispositivo o dirección')? – Andres

+0

@Ares un mensaje de error que parece que está enviando a una ubicación no válida. –

+0

Para enviar datos, el sistema operativo al menos necesita decidir qué interfaz (por ejemplo, 'lo' o' eth0') debería usar. Por lo tanto, debe enlazar a la dirección o enlazar al dispositivo usando la opción de socket 'SO_BINDTODEVICE'. AFAIK el último no es compatible con el paquete 'network' – Yuras

0

Tenía tarea similar y la única manera de que pudiera venir con FFI está utilizando. La corriente network (2.4.0.1 al momento de la escritura) solo conoce tres familias de SockAddr, ninguna de las cuales es adecuada para enchufes AF_PACKET. Lo que se requiere es struct sockaddr_ll, que no está disponible.

Hubo un patch that added SockAddrRaw, pero a pesar de que fue merget parece que finalmente se perdió.

Mi código está sucio (lo siento, ese es mi primer código FFI "serio"), pero pensé que lo compartiría de todos modos.

Primero, creé BindPacketHack.hsc.

{-# LANGUAGE CPP, ForeignFunctionInterface #-} 

module BindPacketHack (bindPacket) where 

import Foreign 
import Foreign.C.Types 
import Foreign.C.String 
import Network.Socket 
import Network.Socket.Internal (zeroMemory) 

#include <sys/socket.h> 
#include <netpacket/packet.h> 
#include <net/if.h> 

data SockAddrLL = SockAddrLL { family :: CShort 
          , protocol :: CShort 
          , ifindex :: CInt } 
    deriving (Eq, Show) 

instance Storable SockAddrLL where 
    sizeOf _ = (#size struct sockaddr_ll) 
    alignment _ = alignment (undefined :: CInt) 
    peek _  = error "peek is not implemented, sorry" 
    poke ptr sa = do 
     zeroMemory ptr (fromIntegral $ sizeOf sa) 
     (#poke struct sockaddr_ll, sll_family) ptr (family sa) 
     (#poke struct sockaddr_ll, sll_protocol) ptr (protocol sa) 
     (#poke struct sockaddr_ll, sll_ifindex) ptr (ifindex sa) 


foreign import ccall unsafe "if_nametoindex" 
    c_if_nametoindex :: CString -> IO CInt 

ifaceIndex :: String -> IO CInt 
ifaceIndex name = withCString name c_if_nametoindex 


foreign import ccall unsafe "bind" 
    c_bind_ll :: CInt -> Ptr SockAddrLL -> CInt -> IO CInt 

bindLL :: Socket -> SockAddrLL -> IO Integer 
bindLL s sa = alloca $ \ptr -> do 
    poke ptr sa 
    ret <- c_bind_ll (fdSocket s) ptr (fromIntegral $ sizeOf sa) 
    return $ toInteger ret 


bindPacket :: Socket -> ProtocolNumber -> String -> IO() 
bindPacket s proto ifname = do 
    ifindex <- ifaceIndex ifname 
    bindLL s (sockAddrLL (fromIntegral proto) ifindex) 
    return() 

Como se puede notar, la SockAddrLL es incompleta y por lo tanto mi aplicación es bastante limitada. Esto se debe a que solo he necesitado esos tres campos, y el resto se ha reducido a cero. Agregar más es trivial, sin embargo.

Ejecute hsc2hs BindPacketHack.hsc, analizará los archivos de encabezado C y aparecerá un archivo .hs. Ahora puede usarlo así:

s <- socket AF_PACKET Raw eth_PPPoEDiscovery 
setFdOption (Fd $ fdSocket s) CloseOnExec True 
setSocketOption s Broadcast 1 
bindPacket s eth_PPPoEDiscovery "eth0" 
send s rawPPPoEPacket -- don't forget Ethernet header