# SSH MAX Connections

## ]Trix[

How can I setup MAX SSH simultaneus connections per user?

Thanks in advance.

----------

## krolden

iptables would do the trick.

----------

## Kasabian

AFAIK there isn't explicit sshd configuration for that.

As krolden says.. take a look at (example included):

netfilter iplimit module

I hope that help you.

----------

## tnt

is there some netfilter iplimit patch for gentoo sources?

----------

## Kasabian

Here's a little how-to:

By the way, the name has changed to connlimit (no more iplimit).

Runs with no problems with: sys-kernel/gentoo-sources-2.6.18-gentoo-r4  &&  net-firewall/iptables-1.3.5-r4

1. If you are bored, just type the lines below (  :Shocked:  ) or copy & paste them to: /usr/src/linux/connlimit.patch:

```
diff -Naur linux-2.6.18/include/linux/netfilter_ipv4/ipt_connlimit.h linux-2.6.18/include/linux/netfilter_ipv4/ipt_connlimit.h

--- linux-2.6.18/include/linux/netfilter_ipv4/ipt_connlimit.h   1970-01-01 01:00:00.000000000 +0100

+++ linux-2.6.18/include/linux/netfilter_ipv4/ipt_connlimit.h   2006-12-31 15:03:07.000000000 +0100

@@ -0,0 +1,12 @@

+#ifndef _IPT_CONNLIMIT_H

+#define _IPT_CONNLIMIT_H

+

+struct ipt_connlimit_data;

+

+struct ipt_connlimit_info {

+   int limit;

+   int inverse;

+   u_int32_t mask;

+   struct ipt_connlimit_data *data;

+};

+#endif /* _IPT_CONNLIMIT_H */

diff -Naur linux-2.6.18/net/ipv4/netfilter/ipt_connlimit.c linux-2.6.18/net/ipv4/netfilter/ipt_connlimit.c

--- linux-2.6.18/net/ipv4/netfilter/ipt_connlimit.c   1970-01-01 01:00:00.000000000 +0100

+++ linux-2.6.18/net/ipv4/netfilter/ipt_connlimit.c   2006-12-31 14:54:55.000000000 +0100

@@ -0,0 +1,255 @@

+/*

+ * netfilter module to limit the number of parallel tcp

+ * connections per IP address.

+ *   (c) 2000 Gerd Knorr <kraxel@bytesex.org>

+ *   Nov 2002: Martin Bene <martin.bene@icomedias.com>:

+ *      only ignore TIME_WAIT or gone connections

+ *

+ * based on ...

+ *

+ * Kernel module to match connection tracking information.

+ * GPL (C) 1999  Rusty Russell (rusty@rustcorp.com.au).

+ */

+#include <linux/module.h>

+#include <linux/skbuff.h>

+#include <linux/version.h>

+#include <linux/list.h>

+#include <linux/netfilter_ipv4/ip_conntrack.h>

+#include <linux/netfilter_ipv4/ip_conntrack_core.h>

+#include <linux/netfilter_ipv4/ip_conntrack_tcp.h>

+#include <linux/netfilter_ipv4/ip_tables.h>

+#include <linux/netfilter_ipv4/ipt_connlimit.h>

+

+#define DEBUG 0

+

+MODULE_LICENSE("GPL");

+

+/* we'll save the tuples of all connections we care about */

+struct ipt_connlimit_conn

+{

+        struct list_head list;

+   struct ip_conntrack_tuple tuple;

+};

+

+struct ipt_connlimit_data {

+   spinlock_t lock;

+   struct list_head iphash[256];

+};

+

+static inline unsigned ipt_iphash(const unsigned addr)

+{

+   return ((addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff);

+}

+

+static int count_them(struct ipt_connlimit_data *data,

+            u_int32_t addr, u_int32_t mask,

+            struct ip_conntrack *ct)

+{

+#if DEBUG

+   const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv",

+                 "fin_wait", "time_wait", "close", "close_wait",

+                 "last_ack", "listen" };

+#endif

+   int addit = 1, matches = 0;

+   struct ip_conntrack_tuple tuple;

+   struct ip_conntrack_tuple_hash *found;

+   struct ipt_connlimit_conn *conn;

+   struct list_head *hash,*lh;

+

+   spin_lock_bh(&data->lock);

+   tuple = ct->tuplehash[0].tuple;

+   hash = &data->iphash[ipt_iphash(addr & mask)];

+

+   /* check the saved connections */

+   for (lh = hash->next; lh != hash; lh = lh->next) {

+      struct ip_conntrack *found_ct = NULL;

+      conn = list_entry(lh,struct ipt_connlimit_conn,list);

+      found = ip_conntrack_find_get(&conn->tuple,ct);

+       if (found != NULL 

+           && (found_ct = tuplehash_to_ctrack(found)) != NULL

+           && 0 == memcmp(&conn->tuple,&tuple,sizeof(tuple)) 

+           && found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT) {

+         /* Just to be sure we have it only once in the list.

+            We should'nt see tuples twice unless someone hooks this

+            into a table without "-p tcp --syn" */

+         addit = 0;

+      }

+#if DEBUG

+      printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n",

+             ipt_iphash(addr & mask),

+             NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port),

+             NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port),

+             (NULL != found) ? tcp[found_ct->proto.tcp.state] : "gone");

+#endif

+      if (NULL == found) {

+         /* this one is gone */

+         lh = lh->prev;

+         list_del(lh->next);

+         kfree(conn);

+         continue;

+      }

+      if (found_ct->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) {

+         /* we don't care about connections which are

+            closed already -> ditch it */

+         lh = lh->prev;

+         list_del(lh->next);

+         kfree(conn);

+         nf_conntrack_put(&found_ct->ct_general);

+         continue;

+      }

+      if ((addr & mask) == (conn->tuple.src.ip & mask)) {

+         /* same source IP address -> be counted! */

+         matches++;

+      }

+      nf_conntrack_put(&found_ct->ct_general);

+   }

+   if (addit) {

+      /* save the new connection in our list */

+#if DEBUG

+      printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n",

+             ipt_iphash(addr & mask),

+             NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port),

+             NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));

+#endif

+      conn = kmalloc(sizeof(*conn),GFP_ATOMIC);

+      if (NULL == conn) {

+         spin_unlock_bh(&data->lock);

+         return -1;

+      }

+      memset(conn,0,sizeof(*conn));

+      INIT_LIST_HEAD(&conn->list);

+      conn->tuple = tuple;

+      list_add(&conn->list,hash);

+      matches++;

+   }

+   spin_unlock_bh(&data->lock);

+   return matches;

+}

+

+static int

+match(const struct sk_buff *skb,

+      const struct net_device *in,

+      const struct net_device *out,

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)

+      const struct xt_match *match,

+#endif

+      const void *matchinfo,

+      int offset,

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)

+      unsigned int protoff,

+#endif

+      int *hotdrop)

+{

+   const struct ipt_connlimit_info *info = matchinfo;

+   int connections, rv;

+   struct ip_conntrack *ct;

+   enum ip_conntrack_info ctinfo;

+

+   ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);

+   if (NULL == ct) {

+      printk("ipt_connlimit: Oops: invalid ct state ?\n");

+      *hotdrop = 1;

+      return 0;

+   }

+   connections = count_them(info->data,skb->nh.iph->saddr,info->mask,ct);

+   if (-1 == connections) {

+      printk("ipt_connlimit: Hmm, kmalloc failed :-(\n");

+      *hotdrop = 1; /* let's free some memory :-) */

+      return 0;

+   }

+        rv = (info->inverse) ? (connections <= info->limit) : (connections > info->limit);

+#if DEBUG

+   printk("ipt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u "

+          "connections=%d limit=%d match=%s\n",

+          NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask),

+          connections, info->limit, rv?"yes":"no");

+#endif

+

+   return rv;

+}

+

+static int check(const char *tablename,

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)

+       const void *ip_void,

+#else

+       const struct ipt_ip *ip,

+#endif

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)

+       const struct xt_match *match,

+#endif

+       void *matchinfo,

+       unsigned int matchsize,

+       unsigned int hook_mask)

+{

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)

+   const struct ipt_ip *ip = ip_void;

+#endif

+

+   struct ipt_connlimit_info *info = matchinfo;

+   int i;

+

+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)

+   /* verify size */

+   if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info)))

+      return 0;

+#endif

+

+   /* refuse anything but tcp */

+   if (ip->proto != IPPROTO_TCP)

+      return 0;

+

+   /* init private data */

+   info->data = kmalloc(sizeof(struct ipt_connlimit_data),GFP_KERNEL);

+   spin_lock_init(&(info->data->lock));

+   for (i = 0; i < 256; i++)

+      INIT_LIST_HEAD(&(info->data->iphash[i]));

+   

+   return 1;

+}

+

+static void destroy(

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)

+          const struct xt_match *match,

+#endif

+          void *matchinfo, unsigned int matchsize)

+{

+   struct ipt_connlimit_info *info = matchinfo;

+   struct ipt_connlimit_conn *conn;

+   struct list_head *hash;

+   int i;

+

+   /* cleanup */

+   for (i = 0; i < 256; i++) {

+      hash = &(info->data->iphash[i]);

+      while (hash != hash->next) {

+         conn = list_entry(hash->next,struct ipt_connlimit_conn,list);

+         list_del(hash->next);

+         kfree(conn);

+      }

+   }

+   kfree(info->data);

+}

+

+static struct ipt_match connlimit_match = { 

+   .name      = "connlimit",

+   .match      = &match,

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)

+   .matchsize   = sizeof(struct ipt_connlimit_info),

+#endif

+   .checkentry   = &check,

+   .destroy   = &destroy,

+   .me      = THIS_MODULE

+};

+

+static int __init init(void)

+{

+   return ipt_register_match(&connlimit_match);

+}

+

+static void __exit fini(void)

+{

+   ipt_unregister_match(&connlimit_match);

+}

+

+module_init(init);

+module_exit(fini);

diff -Naur linux-2.6.18/net/ipv4/netfilter/Kconfig linux-2.6.18/net/ipv4/netfilter/Kconfig

--- linux-2.6.18/net/ipv4/netfilter/Kconfig   2006-09-20 05:42:06.000000000 +0200

+++ linux-2.6.18/net/ipv4/netfilter/Kconfig   2006-12-31 15:02:21.000000000 +0100

@@ -340,6 +340,30 @@

      destination IP' or `500pps from any given source IP'  with a single

      IPtables rule.

 

+config IP_NF_MATCH_CONNLIMIT

+   tristate  'Connections/IP limit match support'

+   depends on IP_NF_IPTABLES

+   help

+     This match allows you to restrict the number of parallel TCP

+     connections to a server per client IP address (or address block).

+

+     Examples:

+

+      Allow 2 telnet connections per client host:

+      iptables -A INPUT -p tcp --syn --dport 23 -m connlimit \ 

+         --connlimit-above 2 -j REJECT

+

+      You can also match the other way around:

+      iptables -p tcp --syn --dport 23 -m connlimit ! \

+          --connlimit-above 2 -j ACCEPT

+

+      Or limit the nr of parallel http requests to 16 per class C sized

+      network (24 bit netmask)

+      iptables -p tcp --syn --dport 80 -m connlimit \ 

+         --connlimit-above 16 --connlimit-mask 24 -j REJECT

+   

+     To compile it as a module, choose M here.  If unsure, say N.

+

 # `filter', generic and specific targets

 config IP_NF_FILTER

    tristate "Packet filtering"

diff -Naur linux-2.6.18/net/ipv4/netfilter/Makefile linux-2.6.18/net/ipv4/netfilter/Makefile

--- linux-2.6.18/net/ipv4/netfilter/Makefile   2006-09-20 05:42:06.000000000 +0200

+++ linux-2.6.18/net/ipv4/netfilter/Makefile   2006-12-31 15:02:43.000000000 +0100

@@ -57,6 +57,7 @@

 obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o

 obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o

 obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o

+obj-$(CONFIG_IP_NF_MATCH_CONNLIMIT) += ipt_connlimit.o

 obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o

 obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o

 obj-$(CONFIG_IP_NF_MATCH_DSCP) += ipt_dscp.o
```

2. Go to the kernel source tree, e.g:

 *Quote:*   

> cd /usr/src/linux/

 

3. First, try a "dry-run":

 *Quote:*   

> localhost linux # md5sum connlimit.patch 
> 
> 15911bdc18fcc1bf9157dda0f22d1c99  connlimit.patch
> 
> localhost linux # patch --dry-run -p1 < connlimit.patch 
> ...

 

4. If everything goes well, you should get no errors. So patch it:

 *Quote:*   

> localhost linux # patch -p1 < connlimit.patch 
> 
> patching file include/linux/netfilter_ipv4/ipt_connlimit.h
> 
> patching file net/ipv4/netfilter/ipt_connlimit.c
> ...

 

5. Configure the kernel to your needs, and don't forget to enable connection tracking and connlimit:

```
Networking -->

  Networking options -->

    [*] Network packet filtering (replace ipchains) -->

      IP: Netfilter Configuration -->

        <*> Connection tracking (required for masq/NAT)

         ...

         ...

        <*> IP tables support (required for filtering/masq/NAT)

        <*>   Connections/IP limit match support
```

Now recompile your kernel and reboot into your new kernel.

And voilà!

 *Quote:*   

> localhost linux # iptables -N connlimitlog
> 
> localhost linux # iptables -A connlimitlog -j LOG --log-level 4 --log-prefix "Conn Limit:"
> 
> localhost linux # iptables -A connlimitlog -j REJECT
> ...

 

It works!

Original netfilter-connlimit-files: http://people.netfilter.org/ole/pom/connlimit

----------

## tnt

thank you very much!

that helped a lot !

 :Wink: 

----------

## ]Trix[

Yes thanks a lot.

----------

