2012-04-19 3 views
5

Network.Socket에는 원시 소켓 유형이 있지만 바인딩 소켓 근처에는 "현재 Unix 도메인 소켓과 인터넷 제품군 만 지원됩니다."라는 주석이 있습니다. 어떻게하면 haskell에서 원시 소켓을 사용할 수 있습니까?하스켈의 원시 소켓

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 :

내가 작업 파이썬 코드로, 달성하기 위해 노력하고 무엇

하스켈에서 , 내가 소켓을 만들 수 sock <- socket AF_PACKET Raw 0xFFFF를 사용하지만 bindSocket는 인수로 SockAddr을 필요로 사용할 수있는 생성자에 대한

SockAddrInet PortNumber HostAddress 
SockAddrInet6 PortNumber FlowInfo HostAddress6 ScopeID 
SockAddrUnix String 

그러나이 중 어느 것도 옳지 않은 것으로 보입니다.

EDIT2 : 내가 패킷을 수신있어 Yuras에 의해 코멘트에 덕분에 작동하도록 :

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

EDIT3 : 에 예외 결과 바인딩하지 않고 소켓에서 패킷을 보내려고 :

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) 

커널은 실제로 패킷을 전송할 인터페이스에 대한 단서가 없으므로 의미가 있습니다. 하스켈에서 (원시) 소켓을 인터페이스에 바인딩하는 방법이 있습니까?

+1

... 무엇이 문제입니까? –

+1

['AF_PACKET'] (http://hackage.haskell.org/packages/archive/network/2.3.0.11/doc/html/src/Network-Socket-Internal.html#Family)'가 지원되며 [ 'SOCK_RAW'] (http://hackage.haskell.org/packages/archive/network/2.3.0.11/doc/html/src/Network-Socket.html#SocketType). 나와 똑같은 번역본처럼 보입니다. –

+0

하스 켈에서 이것을하는 방법? – Andres

답변

4

Network.Pcap은 원시 데이터를 보내고받을 수 있습니다.

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

이 인터페이스는 Network.Socket에서 지원됩니다.

+0

사실, 그렇지 않습니다. 나는'Network.Socket'이'AF_PACKET' 소켓 바인딩에 필요한'struct sockaddr_ll'에 대해 아무것도 모른다고 생각합니다. Inet, Inet6 및 Unix 형식의 주소 만 지원됩니다. – drdaeman

1

우선, 원시 소켓을 바인드 할 필요가 없습니다. man 7 raw

raw 소켓은 bind (2) 호출을 사용하여 특정 로컬 주소에 바인딩 될 수 있습니다. 바인딩되어 있지 않으면 지정된 IP 프로토콜을 가진 모든 패킷이 수신됩니다. 당신이 그것을 바인딩 할 경우 원시 소켓 ip 같은 sockaddr 구조를 사용하기 때문에, 당신은 bindSocket을 사용할 수 있습니다

는 사용

원시 소켓은 IP에 정의 된 표준를 sockaddr_in 주소 구조를 사용 (7)

+0

소켓을 바인딩하지 않고 수신 할 수 있지만 보낼 수없는 이유는 무엇입니까 (위의 파이썬 코드에서'[Errno 6]에 해당 장치 또는 주소가 없습니다 ')? – Andres

+0

@Andres 잘못된 위치로 보내는 것처럼 오류 메시지가 들린다. –

+0

데이터 OS를 보내려면 적어도 사용해야하는 인터페이스 (예 :'lo' 또는'eth0')를 결정해야합니다. 따라서'SO_BINDTODEVICE' 소켓 옵션을 사용하여 디바이스에 주소를 지정하거나 bind 할 필요가있다. AFAIK 마지막은'network' 패키지에 의해 지원되지 않습니다. – Yuras

0

내가 할 수있는 유일한 방법은 FFI입니다. 현재 network (작성 당시 2.4.0.1)은 SockAddr 세 군데에 대해서만 알고 있으며 그 중 어느 것도 AF_PACKET 소켓에는 적합하지 않습니다. 필요한 것은 struct sockaddr_ll이며 사용할 수 없습니다.

거기에 patch that added SockAddrRaw가 있었지만, 그것이 병에 걸렸을지라도 그것은 결국 잃어버린 것 같습니다.

내 코드가 더럽습니다 (죄송합니다. 내 첫 "심각한"FFI 코드입니다.)하지만 어쨌든 공유하고 싶습니다.

먼저, 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() 

당신이 알 수 있듯이

SockAddrLL은 불완전하며, 따라서 내 구현은 매우 제한적이다. 나머지 3 개 필드가 필요하기 때문에 나머지는 제로입니다. 더 많은 것을 추가하는 것은 사소한 일입니다.

실행 hsc2hs BindPacketHack.hsc, C 헤더 파일을 구문 분석하고 .hs 파일이 나타납니다. 이제 다음과 같이 사용할 수 있습니다.

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