punchd/raw_socket.nim

92 lines
3.7 KiB
Nim

import asyncdispatch
from nativesockets import
AF_INET,
IPPROTO_TCP,
SOCK_RAW,
bindAddr,
htons
from net import toSockAddr
from posix import setsockopt, SockAddr, Sockaddr_storage, SockLen, SocketHandle
import network_interface
import ip_packet
type
RawSocketError* = object of CatchableError
Sockaddr_ll {.importc: "struct sockaddr_ll", pure, final,
header: "<linux/if_packet.h>".} = object
sll_family: cushort # Always AF_PACKET
sll_protocol: cushort # Physical-layer protocol
sll_ifindex: cint # Interface number
sll_hatype: cushort # ARP hardware type
sll_pkttype: cuchar # Packet type
sll_halen: cuchar # Length of address
sll_addr: array[8, cuchar] # Physical-layer address
Packet_mreq {.importc: "struct packet_mreq", pure, final,
header: "<linux/if_packet.h>".} = object
mr_ifindex: cint
mr_type: cushort
mr_alen: cushort
mr_address: array[8, cuchar]
var
AF_PACKET {.importc: "AF_PACKET", header: "<sys/socket.h>".}: cushort
SOL_PACKET {.importc: "SOL_PACKET", header: "<sys/socket.h>".}: cushort
ETH_P_ALL {.importc: "ETH_P_ALL", header: "<linux/if_ether.h>".}: cushort
IPPROTO_IP {.importc: "IPPROTO_IP", header: "<netinet/in.h>".}: cint
IP_HDRINCL {.importc: "IP_HDRINCL", header: "<netinet/in.h>".}: cint
PACKET_ADD_MEMBERSHIP {.importc: "PACKET_ADD_MEMBERSHIP", header: "<linux/if_packet.h>".}: cushort
PACKET_MR_PROMISC {.importc: "PACKET_MR_PROMISC", header: "<linux/if_packet.h>".}: cushort
proc setupEthernetCapturingSocket*(iface: NetworkInterface): AsyncFD =
# Create a raw packet socket. For accessing outgoing packets we need to use
# ETH_P_ALL which is needed in network byte order, see packet(7) man page.
result = createAsyncNativeSocket(AF_PACKET.cint,
SOCK_RAW.cint,
htons(ETH_P_ALL).cint)
# Limit capturing of packets to the desired network interface, see
# netdevice(7) man page
echo "interface: ", iface.name, ", index: ", iface.index
var sa = Sockaddr_ll(sll_family: AF_PACKET,
sll_protocol: htons(ETH_P_ALL),
sll_ifindex: iface.index)
if bindAddr(result.SocketHandle,
cast[ptr SockAddr](addr sa),
sizeof(Sockaddr_ll).SockLen) != 0:
closeSocket(result)
raise newException(RawSocketError, "cannot bind to interface")
# Enable promiscuous mode, see netdevice(7) man page
var req = Packet_mreq(mr_ifindex: iface.index, mr_type: PACKET_MR_PROMISC)
if setsockopt(result.SocketHandle,
SOL_PACKET.cint,
PACKET_ADD_MEMBERSHIP.cint,
addr req,
sizeof(req).SockLen) != 0:
closeSocket(result)
raise newException(RawSocketError, "cannot enable promiscuous mode")
proc setupTcpInjectingSocket*(): AsyncFD =
# FIXME: would bindAddr be beneficial?
result = createAsyncNativeSocket(AF_INET.cint, SOCK_RAW.cint,
IPPROTO_TCP.cint)
## Tell the kernel to not generate an IP header, as we generate it ourselves,
## see raw(7) man page.
var sockOpt: cint = 1
if setsockopt(result.SocketHandle, IPPROTO_IP, IP_HDRINCL, addr sockOpt,
sizeof(sockOpt).SockLen) != 0:
closeSocket(result)
raise newException(RawSocketError, "cannot set IP_HDRINCL option")
proc injectTcpPacket*(rawFd: AsyncFD, ipPacket: IpPacket) {.async.} =
assert(ipPacket.protocol == tcp)
let packet = serialize(ipPacket)
var sockaddr: Sockaddr_storage
var sockaddrLen: SockLen
toSockAddr(ipPacket.ipAddrDst, ipPacket.tcpPortDst, sockaddr, sockaddrLen)
await rawFd.sendTo(packet.cstring, packet.len,
cast[ptr SockAddr](addr sockaddr), sockaddrLen)