reorganize punch module; add procs to add/remove firewall rules

This commit is contained in:
Christian Ulrich 2020-06-05 21:56:41 +02:00
parent 7341f61e48
commit ce782be7f1
No known key found for this signature in database
GPG Key ID: 8241BE099775A097
3 changed files with 84 additions and 19 deletions

View File

@ -1,7 +1,6 @@
import asyncdispatch, threadpool, osproc
## asyncReadline as discussed at https://github.com/nim-lang/Nim/issues/11564
import asyncdispatch, threadpool
proc asyncReadline*(): Future[string] =
let event = newAsyncEvent()
let future = newFuture[string]("asyncReadline")
@ -15,4 +14,15 @@ proc asyncReadline*(): Future[string] =
addEvent(event, callback)
return future
proc asyncExecCmd*(command: string): Future[int] =
let event = newAsyncEvent()
let future = newFuture[int]("asyncExecCmd")
proc execCmdBackground(event: AsyncEvent, command: string): int =
result = execCmd(command)
event.trigger()
let flowVar = spawn execCmdBackground(event, command)
proc callback(fd: AsyncFD): bool =
future.complete(^flowVar)
true
addEvent(event, callback)
return future

View File

@ -1,7 +1,9 @@
import
asyncdispatch,
asyncutils,
network_interface,
packet_info
packet_info,
strformat
from nativesockets import SOCK_RAW, bindAddr, htons
from posix import ioctl, setsockopt, SockAddr, SockLen, SocketHandle
@ -35,38 +37,38 @@ var
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 punchHoleAsClient*(clientAddress: string,
clientPort: Port,
serverAddress: string,
serverPort: Port) {.async.} =
echo "punchHoleAsClient"
proc setupRawSocket(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.
let rawFd = createAsyncNativeSocket(AF_PACKET.cint,
SOCK_RAW.cint,
htons(ETH_P_ALL).cint)
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
let iface = fromIpAddress(clientAddress)
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(rawFd.SocketHandle,
if bindAddr(result.SocketHandle,
cast[ptr SockAddr](addr sa),
sizeof(Sockaddr_ll).SockLen) != 0:
raise newException(PunchHoleError, "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(rawFd.SocketHandle,
if setsockopt(result.SocketHandle,
SOL_PACKET.cint,
PACKET_ADD_MEMBERSHIP.cint,
addr req,
sizeof(req).SockLen) != 0:
raise newException(PunchHoleError, "cannot enable promiscuous mode")
proc captureSequenceNumber(rawFd: AsyncFD,
clientAddress: string,
clientPort: Port,
serverAddress: string,
serverPort: Port): Future[uint32] {.async.} =
while true:
let packet = await rawFd.recv(4000)
if packet == "":
@ -78,8 +80,61 @@ proc punchHoleAsClient*(clientAddress: string,
packetInfo.tcpPortSrc.int == clientPort.int and
packetInfo.tcpIpDst == serverAddress and
packetInfo.tcpPortDst.int == serverPort.int:
echo "captured packet, sequence number: ", packetInfo.tcpSeqNumber
break
return packetInfo.tcpSeqNumber
proc addFirewallRule(clientAddress: string,
clientPort: Port,
serverAddress: string,
serverPort: Port) {.async.} =
let firewall_cmd = fmt"""iptables -A INPUT \
-d {clientAddress} \
-p icmp \
--icmp-type time-exceeded \
-m conntrack \
--ctstate RELATED \
--ctproto tcp \
--ctorigsrc {clientAddress} \
--ctorigsrcport {clientPort.int} \
--ctorigdst {serverAddress} \
--ctorigdstport {serverPort.int} \
-j DROP"""
let exitcode = await asyncExecCmd(firewall_cmd)
if exitcode != 0:
raise newException(PunchHoleError, "cannot add firewall rule")
proc deleteFirewallRule(clientAddress: string,
clientPort: Port,
serverAddress: string,
serverPort: Port) {.async.} =
let firewall_cmd = fmt"""iptables -D INPUT \
-d {clientAddress} \
-p icmp \
--icmp-type time-exceeded \
-m conntrack \
--ctstate RELATED \
--ctproto tcp \
--ctorigsrc {clientAddress} \
--ctorigsrcport {clientPort.int} \
--ctorigdst {serverAddress} \
--ctorigdstport {serverPort.int} \
-j DROP"""
let exitcode = await asyncExecCmd(firewall_cmd)
if exitcode != 0:
raise newException(PunchHoleError, "cannot delete firewall rule")
proc punchHoleAsClient*(clientAddress: string,
clientPort: Port,
serverAddress: string,
serverPort: Port) {.async.} =
let iface = fromIpAddress(clientAddress)
let rawFd = setupRawSocket(iface)
let seqNumber = await captureSequenceNumber(rawFd,
clientAddress,
clientPort,
serverAddress,
serverPort)
echo "captured sequence number: ", seqNumber
await addFirewallRule(clientAddress, clientPort, serverAddress, serverPort)
await deleteFirewallRule(clientAddress, clientPort, serverAddress, serverPort)
closeSocket(rawFd)

View File

@ -2,7 +2,7 @@ import
net,
asyncnet,
asyncdispatch,
asyncreadline,
asyncutils,
punch
from strutils import format, join