reorganize punch module; add procs to add/remove firewall rules
This commit is contained in:
parent
7341f61e48
commit
ce782be7f1
|
@ -1,7 +1,6 @@
|
||||||
|
import asyncdispatch, threadpool, osproc
|
||||||
|
|
||||||
## asyncReadline as discussed at https://github.com/nim-lang/Nim/issues/11564
|
## asyncReadline as discussed at https://github.com/nim-lang/Nim/issues/11564
|
||||||
|
|
||||||
import asyncdispatch, threadpool
|
|
||||||
|
|
||||||
proc asyncReadline*(): Future[string] =
|
proc asyncReadline*(): Future[string] =
|
||||||
let event = newAsyncEvent()
|
let event = newAsyncEvent()
|
||||||
let future = newFuture[string]("asyncReadline")
|
let future = newFuture[string]("asyncReadline")
|
||||||
|
@ -15,4 +14,15 @@ proc asyncReadline*(): Future[string] =
|
||||||
addEvent(event, callback)
|
addEvent(event, callback)
|
||||||
return future
|
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
|
83
punch.nim
83
punch.nim
|
@ -1,7 +1,9 @@
|
||||||
import
|
import
|
||||||
asyncdispatch,
|
asyncdispatch,
|
||||||
|
asyncutils,
|
||||||
network_interface,
|
network_interface,
|
||||||
packet_info
|
packet_info,
|
||||||
|
strformat
|
||||||
|
|
||||||
from nativesockets import SOCK_RAW, bindAddr, htons
|
from nativesockets import SOCK_RAW, bindAddr, htons
|
||||||
from posix import ioctl, setsockopt, SockAddr, SockLen, SocketHandle
|
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_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
|
PACKET_MR_PROMISC {.importc: "PACKET_MR_PROMISC", header: "<linux/if_packet.h>".}: cushort
|
||||||
|
|
||||||
proc punchHoleAsClient*(clientAddress: string,
|
proc setupRawSocket(iface: NetworkInterface): AsyncFD =
|
||||||
clientPort: Port,
|
|
||||||
serverAddress: string,
|
|
||||||
serverPort: Port) {.async.} =
|
|
||||||
echo "punchHoleAsClient"
|
|
||||||
# Create a raw packet socket. For accessing outgoing packets we need to use
|
# 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.
|
# ETH_P_ALL which is needed in network byte order, see packet(7) man page.
|
||||||
let rawFd = createAsyncNativeSocket(AF_PACKET.cint,
|
result = createAsyncNativeSocket(AF_PACKET.cint,
|
||||||
SOCK_RAW.cint,
|
SOCK_RAW.cint,
|
||||||
htons(ETH_P_ALL).cint)
|
htons(ETH_P_ALL).cint)
|
||||||
|
|
||||||
# Limit capturing of packets to the desired network interface, see
|
# Limit capturing of packets to the desired network interface, see
|
||||||
# netdevice(7) man page
|
# netdevice(7) man page
|
||||||
let iface = fromIpAddress(clientAddress)
|
|
||||||
echo "interface: ", iface.name, ", index: ", iface.index
|
echo "interface: ", iface.name, ", index: ", iface.index
|
||||||
var sa = Sockaddr_ll(sll_family: AF_PACKET,
|
var sa = Sockaddr_ll(sll_family: AF_PACKET,
|
||||||
sll_protocol: htons(ETH_P_ALL),
|
sll_protocol: htons(ETH_P_ALL),
|
||||||
sll_ifindex: iface.index)
|
sll_ifindex: iface.index)
|
||||||
if bindAddr(rawFd.SocketHandle,
|
if bindAddr(result.SocketHandle,
|
||||||
cast[ptr SockAddr](addr sa),
|
cast[ptr SockAddr](addr sa),
|
||||||
sizeof(Sockaddr_ll).SockLen) != 0:
|
sizeof(Sockaddr_ll).SockLen) != 0:
|
||||||
raise newException(PunchHoleError, "cannot bind to interface")
|
raise newException(PunchHoleError, "cannot bind to interface")
|
||||||
|
|
||||||
# Enable promiscuous mode, see netdevice(7) man page
|
# Enable promiscuous mode, see netdevice(7) man page
|
||||||
var req = Packet_mreq(mr_ifindex: iface.index, mr_type: PACKET_MR_PROMISC)
|
var req = Packet_mreq(mr_ifindex: iface.index, mr_type: PACKET_MR_PROMISC)
|
||||||
if setsockopt(rawFd.SocketHandle,
|
if setsockopt(result.SocketHandle,
|
||||||
SOL_PACKET.cint,
|
SOL_PACKET.cint,
|
||||||
PACKET_ADD_MEMBERSHIP.cint,
|
PACKET_ADD_MEMBERSHIP.cint,
|
||||||
addr req,
|
addr req,
|
||||||
sizeof(req).SockLen) != 0:
|
sizeof(req).SockLen) != 0:
|
||||||
raise newException(PunchHoleError, "cannot enable promiscuous mode")
|
raise newException(PunchHoleError, "cannot enable promiscuous mode")
|
||||||
|
|
||||||
|
proc captureSequenceNumber(rawFd: AsyncFD,
|
||||||
|
clientAddress: string,
|
||||||
|
clientPort: Port,
|
||||||
|
serverAddress: string,
|
||||||
|
serverPort: Port): Future[uint32] {.async.} =
|
||||||
while true:
|
while true:
|
||||||
let packet = await rawFd.recv(4000)
|
let packet = await rawFd.recv(4000)
|
||||||
if packet == "":
|
if packet == "":
|
||||||
|
@ -78,8 +80,61 @@ proc punchHoleAsClient*(clientAddress: string,
|
||||||
packetInfo.tcpPortSrc.int == clientPort.int and
|
packetInfo.tcpPortSrc.int == clientPort.int and
|
||||||
packetInfo.tcpIpDst == serverAddress and
|
packetInfo.tcpIpDst == serverAddress and
|
||||||
packetInfo.tcpPortDst.int == serverPort.int:
|
packetInfo.tcpPortDst.int == serverPort.int:
|
||||||
echo "captured packet, sequence number: ", packetInfo.tcpSeqNumber
|
return packetInfo.tcpSeqNumber
|
||||||
break
|
|
||||||
|
|
||||||
|
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)
|
closeSocket(rawFd)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import
|
||||||
net,
|
net,
|
||||||
asyncnet,
|
asyncnet,
|
||||||
asyncdispatch,
|
asyncdispatch,
|
||||||
asyncreadline,
|
asyncutils,
|
||||||
punch
|
punch
|
||||||
|
|
||||||
from strutils import format, join
|
from strutils import format, join
|
||||||
|
|
Loading…
Reference in New Issue