Re: SCTP Trasport Loadbalancing Support for IPVS

From: Venkata Mohan Reddy
Date: Wed Feb 17 2010 - 13:49:01 EST


Hi Simon,

I have addressed all your review comments and re-posting the patch.

Thanks,
Mohan Reddy

Signed-off-by : Venkata Mohan Reddy Koppula <mohanreddykv@xxxxxxxxx>


diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index a816c37..fe82b1e 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -225,6 +225,26 @@ enum {
};

/*
+ * SCTP State Values
+ */
+enum ip_vs_sctp_states {
+ IP_VS_SCTP_S_NONE,
+ IP_VS_SCTP_S_INIT_CLI,
+ IP_VS_SCTP_S_INIT_SER,
+ IP_VS_SCTP_S_INIT_ACK_CLI,
+ IP_VS_SCTP_S_INIT_ACK_SER,
+ IP_VS_SCTP_S_ECHO_CLI,
+ IP_VS_SCTP_S_ECHO_SER,
+ IP_VS_SCTP_S_ESTABLISHED,
+ IP_VS_SCTP_S_SHUT_CLI,
+ IP_VS_SCTP_S_SHUT_SER,
+ IP_VS_SCTP_S_SHUT_ACK_CLI,
+ IP_VS_SCTP_S_SHUT_ACK_SER,
+ IP_VS_SCTP_S_CLOSED,
+ IP_VS_SCTP_S_LAST
+};
+
+/*
* Delta sequence info structure
* Each ip_vs_conn has 2 (output AND input seq. changes).
* Only used in the VS/NAT.
@@ -741,7 +761,7 @@ extern struct ip_vs_protocol ip_vs_protocol_udp;
extern struct ip_vs_protocol ip_vs_protocol_icmp;
extern struct ip_vs_protocol ip_vs_protocol_esp;
extern struct ip_vs_protocol ip_vs_protocol_ah;
-
+extern struct ip_vs_protocol ip_vs_protocol_sctp;

/*
* Registering/unregistering scheduler functions
diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig
index c71e543..6dba1e8 100644
--- a/net/netfilter/ipvs/Kconfig
+++ b/net/netfilter/ipvs/Kconfig
@@ -104,6 +104,13 @@ config IP_VS_PROTO_AH
This option enables support for load balancing AH (Authentication
Header) transport protocol. Say Y if unsure.

+config IP_VS_PROTO_SCTP
+ bool "SCTP load balancing support"
+ select LIBCRC32C
+ ---help---
+ This option enables support for load balancing SCTP transport
+ protocol. Say Y if unsure.
+
comment "IPVS scheduler"

config IP_VS_RR
diff --git a/net/netfilter/ipvs/Makefile b/net/netfilter/ipvs/Makefile
index 73a46fe..e3baefd 100644
--- a/net/netfilter/ipvs/Makefile
+++ b/net/netfilter/ipvs/Makefile
@@ -7,6 +7,7 @@ ip_vs_proto-objs-y :=
ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_TCP) += ip_vs_proto_tcp.o
ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_UDP) += ip_vs_proto_udp.o
ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_AH_ESP) += ip_vs_proto_ah_esp.o
+ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_SCTP) += ip_vs_proto_sctp.o

ip_vs-objs := ip_vs_conn.o ip_vs_core.o ip_vs_ctl.o ip_vs_sched.o \
ip_vs_xmit.o ip_vs_app.o ip_vs_sync.o \
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 847ffca..72e96d8 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -31,6 +31,7 @@
#include <linux/kernel.h>
#include <linux/ip.h>
#include <linux/tcp.h>
+#include <linux/sctp.h>
#include <linux/icmp.h>

#include <net/ip.h>
@@ -81,6 +82,8 @@ const char *ip_vs_proto_name(unsigned proto)
return "UDP";
case IPPROTO_TCP:
return "TCP";
+ case IPPROTO_SCTP:
+ return "SCTP";
case IPPROTO_ICMP:
return "ICMP";
#ifdef CONFIG_IP_VS_IPV6
@@ -589,8 +592,9 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
ip_send_check(ciph);
}

- /* the TCP/UDP port */
- if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol) {
+ /* the TCP/UDP/SCTP port */
+ if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
+ IPPROTO_SCTP == ciph->protocol) {
__be16 *ports = (void *)ciph + ciph->ihl*4;

if (inout)
@@ -630,8 +634,9 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
ciph->saddr = cp->daddr.in6;
}

- /* the TCP/UDP port */
- if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr) {
+ /* the TCP/UDP/SCTP port */
+ if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr ||
+ IPPROTO_SCTP == ciph->nexthdr) {
__be16 *ports = (void *)ciph + sizeof(struct ipv6hdr);

if (inout)
@@ -679,7 +684,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
goto out;
}

- if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol)
+ if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+ IPPROTO_SCTP == protocol)
offset += 2 * sizeof(__u16);
if (!skb_make_writable(skb, offset))
goto out;
@@ -857,6 +863,21 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related)
}
#endif

+/*
+ * Check if sctp chunc is ABORT chunk
+ */
+static inline int is_sctp_abort(const struct sk_buff *skb, int nh_len)
+{
+ sctp_chunkhdr_t *sch, schunk;
+ sch = skb_header_pointer(skb, nh_len + sizeof(sctp_sctphdr_t),
+ sizeof(schunk), &schunk);
+ if (sch == NULL)
+ return 0;
+ if (sch->type == SCTP_CID_ABORT)
+ return 1;
+ return 0;
+}
+
static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len)
{
struct tcphdr _tcph, *th;
@@ -999,7 +1020,8 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
if (unlikely(!cp)) {
if (sysctl_ip_vs_nat_icmp_send &&
(pp->protocol == IPPROTO_TCP ||
- pp->protocol == IPPROTO_UDP)) {
+ pp->protocol == IPPROTO_UDP ||
+ pp->protocol == IPPROTO_SCTP)) {
__be16 _ports[2], *pptr;

pptr = skb_header_pointer(skb, iph.len,
@@ -1014,8 +1036,13 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
* existing entry if it is not RST
* packet or not TCP packet.
*/
- if (iph.protocol != IPPROTO_TCP
- || !is_tcp_reset(skb, iph.len)) {
+ if ((iph.protocol != IPPROTO_TCP &&
+ iph.protocol != IPPROTO_SCTP)
+ || ((iph.protocol == IPPROTO_TCP
+ && !is_tcp_reset(skb, iph.len))
+ || (iph.protocol == IPPROTO_SCTP
+ && !is_sctp_abort(skb,
+ iph.len)))) {
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
icmpv6_send(skb,
@@ -1235,7 +1262,8 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)

/* do the statistics and put it back */
ip_vs_in_stats(cp, skb);
- if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr)
+ if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr ||
+ IPPROTO_SCTP == cih->nexthdr)
offset += 2 * sizeof(__u16);
verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset);
/* do not touch skb anymore */
@@ -1358,6 +1386,21 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
* encorage the standby servers to update the connections timeout
*/
pkts = atomic_add_return(1, &cp->in_pkts);
+ if (af == AF_INET && (ip_vs_sync_state & IP_VS_STATE_MASTER) &&
+ cp->protocol == IPPROTO_SCTP) {
+ if ((cp->state == IP_VS_SCTP_S_ESTABLISHED &&
+ (atomic_read(&cp->in_pkts) %
+ sysctl_ip_vs_sync_threshold[1]
+ == sysctl_ip_vs_sync_threshold[0])) ||
+ (cp->old_state != cp->state &&
+ ((cp->state == IP_VS_SCTP_S_CLOSED) ||
+ (cp->state == IP_VS_SCTP_S_SHUT_ACK_CLI) ||
+ (cp->state == IP_VS_SCTP_S_SHUT_ACK_SER)))) {
+ ip_vs_sync_conn(cp);
+ goto out;
+ }
+ }
+
if (af == AF_INET &&
(ip_vs_sync_state & IP_VS_STATE_MASTER) &&
(((cp->protocol != IPPROTO_TCP ||
@@ -1370,6 +1413,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
(cp->state == IP_VS_TCP_S_CLOSE_WAIT) ||
(cp->state == IP_VS_TCP_S_TIME_WAIT)))))
ip_vs_sync_conn(cp);
+out:
cp->old_state = cp->state;

ip_vs_conn_put(cp);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 93420ea..48f4f67 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2128,8 +2128,9 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
}
}

- /* Check for valid protocol: TCP or UDP, even for fwmark!=0 */
- if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP) {
+ /* Check for valid protocol: TCP or UDP or SCTP, even for fwmark!=0 */
+ if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP &&
+ usvc.protocol != IPPROTO_SCTP) {
pr_err("set_ctl: invalid protocol: %d %pI4:%d %s\n",
usvc.protocol, &usvc.addr.ip,
ntohs(usvc.port), usvc.sched_name);
diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c
index 3e76716..0e58455 100644
--- a/net/netfilter/ipvs/ip_vs_proto.c
+++ b/net/netfilter/ipvs/ip_vs_proto.c
@@ -257,6 +257,9 @@ int __init ip_vs_protocol_init(void)
#ifdef CONFIG_IP_VS_PROTO_UDP
REGISTER_PROTOCOL(&ip_vs_protocol_udp);
#endif
+#ifdef CONFIG_IP_VS_PROTO_SCTP
+ REGISTER_PROTOCOL(&ip_vs_protocol_sctp);
+#endif
#ifdef CONFIG_IP_VS_PROTO_AH
REGISTER_PROTOCOL(&ip_vs_protocol_ah);
#endif
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
new file mode 100644
index 0000000..c9a3f7a
--- /dev/null
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -0,0 +1,1183 @@
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/sctp.h>
+#include <net/ip.h>
+#include <net/ip6_checksum.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/sctp/checksum.h>
+#include <net/ip_vs.h>
+
+
+static struct ip_vs_conn *
+sctp_conn_in_get(int af,
+ const struct sk_buff *skb,
+ struct ip_vs_protocol *pp,
+ const struct ip_vs_iphdr *iph,
+ unsigned int proto_off,
+ int inverse)
+{
+ __be16 _ports[2], *pptr;
+
+ pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
+ if (pptr == NULL)
+ return NULL;
+
+ if (likely(!inverse))
+ return ip_vs_conn_in_get(af, iph->protocol,
+ &iph->saddr, pptr[0],
+ &iph->daddr, pptr[1]);
+ else
+ return ip_vs_conn_in_get(af, iph->protocol,
+ &iph->daddr, pptr[1],
+ &iph->saddr, pptr[0]);
+}
+
+static struct ip_vs_conn *
+sctp_conn_out_get(int af,
+ const struct sk_buff *skb,
+ struct ip_vs_protocol *pp,
+ const struct ip_vs_iphdr *iph,
+ unsigned int proto_off,
+ int inverse)
+{
+ __be16 _ports[2], *pptr;
+
+ pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
+ if (pptr == NULL)
+ return NULL;
+
+ if (likely(!inverse))
+ return ip_vs_conn_out_get(af, iph->protocol,
+ &iph->saddr, pptr[0],
+ &iph->daddr, pptr[1]);
+ else
+ return ip_vs_conn_out_get(af, iph->protocol,
+ &iph->daddr, pptr[1],
+ &iph->saddr, pptr[0]);
+}
+
+static int
+sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
+ int *verdict, struct ip_vs_conn **cpp)
+{
+ struct ip_vs_service *svc;
+ sctp_chunkhdr_t _schunkh, *sch;
+ sctp_sctphdr_t *sh, _sctph;
+ struct ip_vs_iphdr iph;
+
+ ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
+
+ sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph);
+ if (sh == NULL)
+ return 0;
+
+ sch = skb_header_pointer(skb, iph.len + sizeof(sctp_sctphdr_t),
+ sizeof(_schunkh), &_schunkh);
+ if (sch == NULL)
+ return 0;
+
+ if ((sch->type == SCTP_CID_INIT) &&
+ (svc = ip_vs_service_get(af, skb->mark, iph.protocol,
+ &iph.daddr, sh->dest))) {
+ if (ip_vs_todrop()) {
+ /*
+ * It seems that we are very loaded.
+ * We have to drop this packet :(
+ */
+ ip_vs_service_put(svc);
+ *verdict = NF_DROP;
+ return 0;
+ }
+ /*
+ * Let the virtual server select a real server for the
+ * incoming connection, and create a connection entry.
+ */
+ *cpp = ip_vs_schedule(svc, skb);
+ if (!*cpp) {
+ *verdict = ip_vs_leave(svc, skb, pp);
+ return 0;
+ }
+ ip_vs_service_put(svc);
+ }
+
+ return 1;
+}
+
+static int
+sctp_snat_handler(struct sk_buff *skb,
+ struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+{
+ sctp_sctphdr_t *sctph;
+ unsigned int sctphoff;
+ __be32 crc32;
+
+#ifdef CONFIG_IP_VS_IPV6
+ if (cp->af == AF_INET6)
+ sctphoff = sizeof(struct ipv6hdr);
+ else
+#endif
+ sctphoff = ip_hdrlen(skb);
+
+ /* csum_check requires unshared skb */
+ if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
+ return 0;
+
+ if (unlikely(cp->app != NULL)) {
+ /* Some checks before mangling */
+ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
+ return 0;
+
+ /* Call application helper if needed */
+ if (!ip_vs_app_pkt_out(cp, skb))
+ return 0;
+ }
+
+ sctph = (void *) skb_network_header(skb) + sctphoff;
+ sctph->source = cp->vport;
+
+ /* Calculate the checksum */
+ crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff);
+ for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next)
+ crc32 = sctp_update_cksum((u8 *) skb->data, skb_headlen(skb),
+ crc32);
+ crc32 = sctp_end_cksum(crc32);
+ sctph->checksum = crc32;
+
+ return 1;
+}
+
+static int
+sctp_dnat_handler(struct sk_buff *skb,
+ struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+{
+
+ sctp_sctphdr_t *sctph;
+ unsigned int sctphoff;
+ __be32 crc32;
+
+#ifdef CONFIG_IP_VS_IPV6
+ if (cp->af == AF_INET6)
+ sctphoff = sizeof(struct ipv6hdr);
+ else
+#endif
+ sctphoff = ip_hdrlen(skb);
+
+ /* csum_check requires unshared skb */
+ if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
+ return 0;
+
+ if (unlikely(cp->app != NULL)) {
+ /* Some checks before mangling */
+ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
+ return 0;
+
+ /* Call application helper if needed */
+ if (!ip_vs_app_pkt_out(cp, skb))
+ return 0;
+ }
+
+ sctph = (void *) skb_network_header(skb) + sctphoff;
+ sctph->dest = cp->dport;
+
+ /* Calculate the checksum */
+ crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff);
+ for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next)
+ crc32 = sctp_update_cksum((u8 *) skb->data, skb_headlen(skb),
+ crc32);
+ crc32 = sctp_end_cksum(crc32);
+ sctph->checksum = crc32;
+
+ return 1;
+}
+
+static int
+sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
+{
+ struct sk_buff *list = skb_shinfo(skb)->frag_list;
+ unsigned int sctphoff;
+ struct sctphdr *sh, _sctph;
+ __le32 cmp;
+ __le32 val;
+ __u32 tmp;
+
+#ifdef CONFIG_IP_VS_IPV6
+ if (af == AF_INET6)
+ sctphoff = sizeof(struct ipv6hdr);
+ else
+#endif
+ sctphoff = ip_hdrlen(skb);
+
+ sh = skb_header_pointer(skb, sctphoff, sizeof(_sctph), &_sctph);
+ if (sh == NULL)
+ return 0;
+
+ cmp = sh->checksum;
+
+ tmp = sctp_start_cksum((__u8 *) sh, skb_headlen(skb));
+ for (; list; list = list->next)
+ tmp = sctp_update_cksum((__u8 *) list->data,
+ skb_headlen(list), tmp);
+
+ val = sctp_end_cksum(tmp);
+
+ if (val != cmp) {
+ /* CRC failure, dump it. */
+ IP_VS_DBG_RL_PKT(0, pp, skb, 0,
+ "Failed checksum for");
+ return 0;
+ }
+ return 1;
+}
+
+struct ipvs_sctp_nextstate {
+ int next_state;
+};
+enum ipvs_sctp_event_t {
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_DATA_SER,
+ IP_VS_SCTP_EVE_INIT_CLI,
+ IP_VS_SCTP_EVE_INIT_SER,
+ IP_VS_SCTP_EVE_INIT_ACK_CLI,
+ IP_VS_SCTP_EVE_INIT_ACK_SER,
+ IP_VS_SCTP_EVE_COOKIE_ECHO_CLI,
+ IP_VS_SCTP_EVE_COOKIE_ECHO_SER,
+ IP_VS_SCTP_EVE_COOKIE_ACK_CLI,
+ IP_VS_SCTP_EVE_COOKIE_ACK_SER,
+ IP_VS_SCTP_EVE_ABORT_CLI,
+ IP_VS_SCTP_EVE__ABORT_SER,
+ IP_VS_SCTP_EVE_SHUT_CLI,
+ IP_VS_SCTP_EVE_SHUT_SER,
+ IP_VS_SCTP_EVE_SHUT_ACK_CLI,
+ IP_VS_SCTP_EVE_SHUT_ACK_SER,
+ IP_VS_SCTP_EVE_SHUT_COM_CLI,
+ IP_VS_SCTP_EVE_SHUT_COM_SER,
+ IP_VS_SCTP_EVE_LAST
+};
+
+static enum ipvs_sctp_event_t sctp_events[255] = {
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_INIT_CLI,
+ IP_VS_SCTP_EVE_INIT_ACK_CLI,
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_ABORT_CLI,
+ IP_VS_SCTP_EVE_SHUT_CLI,
+ IP_VS_SCTP_EVE_SHUT_ACK_CLI,
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_COOKIE_ECHO_CLI,
+ IP_VS_SCTP_EVE_COOKIE_ACK_CLI,
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_DATA_CLI,
+ IP_VS_SCTP_EVE_SHUT_COM_CLI,
+};
+
+static struct ipvs_sctp_nextstate
+ sctp_states_table[IP_VS_SCTP_S_LAST][IP_VS_SCTP_EVE_LAST] = {
+ /*
+ * STATE : IP_VS_SCTP_S_NONE
+ */
+ /*next state *//*event */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ },
+ },
+ /*
+ * STATE : IP_VS_SCTP_S_INIT_CLI
+ * Cient sent INIT and is waiting for reply from server(In ECHO_WAIT)
+ */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_ECHO_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_INIT_SER
+ * Server sent INIT and waiting for INIT ACK from the client
+ */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_INIT_ACK_CLI
+ * Client sent INIT ACK and waiting for ECHO from the server
+ */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK has been resent by the client, let us stay is in
+ * the same state
+ */
+ {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ /*
+ * INIT_ACK sent by the server, close the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * ECHO by client, it should not happen, close the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ /*
+ * ECHO by server, this is what we are expecting, move to ECHO_SER
+ */
+ {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, it should not happen, close the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ /*
+ * Unexpected COOKIE ACK from server, staty in the same state
+ */
+ {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_INIT_ACK_SER
+ * Server sent INIT ACK and waiting for ECHO from the client
+ */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * Unexpected INIT_ACK by the client, let us close the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ /*
+ * INIT_ACK resent by the server, let us move to same state
+ */
+ {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client send the ECHO, this is what we are expecting,
+ * move to ECHO_CLI
+ */
+ {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ /*
+ * ECHO received from the server, Not sure what to do,
+ * let us close it
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, let us stay in the same state
+ */
+ {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ /*
+ * COOKIE ACK from server, hmm... this should not happen, lets close
+ * the connection.
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_ECHO_CLI
+ * Cient sent ECHO and waiting COOKEI ACK from the Server
+ */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK has been by the client, let us close the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ /*
+ * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
+ * âIf an INIT ACK is received by an endpoint in any state other
+ * than the COOKIE-WAIT state, the endpoint should discard the
+ * INIT ACK chunkâ. Stay in the same state
+ */
+ {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client resent the ECHO, let us stay in the same state
+ */
+ {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ /*
+ * ECHO received from the server, Not sure what to do,
+ * let us close it
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, this shoud not happen, let's close the
+ * connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ /*
+ * COOKIE ACK from server, this is what we are awaiting,lets move to
+ * ESTABLISHED.
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_ECHO_SER
+ * Server sent ECHO and waiting COOKEI ACK from the client
+ */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
+ * âIf an INIT ACK is received by an endpoint in any state other
+ * than the COOKIE-WAIT state, the endpoint should discard the
+ * INIT ACK chunkâ. Stay in the same state
+ */
+ {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ /*
+ * INIT_ACK has been by the server, let us close the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client sent the ECHO, not sure what to do, let's close the
+ * connection.
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ /*
+ * ECHO resent by the server, stay in the same state
+ */
+ {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, this is what we are expecting, let's move
+ * to ESTABLISHED.
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ /*
+ * COOKIE ACK from server, this should not happen, lets close the
+ * connection.
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_ESTABLISHED
+ * Association established
+ */
+ {{IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
+ * âIf an INIT ACK is received by an endpoint in any state other
+ * than the COOKIE-WAIT state, the endpoint should discard the
+ * INIT ACK chunkâ. Stay in the same state
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
+ * peer and peer shall move to the ESTABISHED. if it doesn't handle
+ * it will send ERROR chunk. So, stay in the same state
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, not sure what to do stay in the same state
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ /*
+ * SHUTDOWN from the client, move to SHUDDOWN_CLI
+ */
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ /*
+ * SHUTDOWN from the server, move to SHUTDOWN_SER
+ */
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ /*
+ * client sent SHUDTDOWN_ACK, this should not happen, let's close
+ * the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_SHUT_CLI
+ * SHUTDOWN sent from the client, waitinf for SHUT ACK from the server
+ */
+ /*
+ * We recieved the data chuck, keep the state unchanged. I assume
+ * that still data chuncks can be received by both the peers in
+ * SHUDOWN state
+ */
+
+ {{IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
+ * âIf an INIT ACK is received by an endpoint in any state other
+ * than the COOKIE-WAIT state, the endpoint should discard the
+ * INIT ACK chunkâ. Stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
+ * peer and peer shall move to the ESTABISHED. if it doesn't handle
+ * it will send ERROR chunk. So, stay in the same state
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, not sure what to do stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ /*
+ * SHUTDOWN resent from the client, move to SHUDDOWN_CLI
+ */
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ /*
+ * SHUTDOWN from the server, move to SHUTDOWN_SER
+ */
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ /*
+ * client sent SHUDTDOWN_ACK, this should not happen, let's close
+ * the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ /*
+ * Server sent SHUTDOWN ACK, this is what we are expecting, let's move
+ * to SHUDOWN_ACK_SER
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ /*
+ * SHUTDOWN COM from client, this should not happen, let's close the
+ * connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_SHUT_SER
+ * SHUTDOWN sent from the server, waitinf for SHUTDOWN ACK from client
+ */
+ /*
+ * We recieved the data chuck, keep the state unchanged. I assume
+ * that still data chuncks can be received by both the peers in
+ * SHUDOWN state
+ */
+
+ {{IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
+ * âIf an INIT ACK is received by an endpoint in any state other
+ * than the COOKIE-WAIT state, the endpoint should discard the
+ * INIT ACK chunkâ. Stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
+ * peer and peer shall move to the ESTABISHED. if it doesn't handle
+ * it will send ERROR chunk. So, stay in the same state
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, not sure what to do stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ /*
+ * SHUTDOWN resent from the client, move to SHUDDOWN_CLI
+ */
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ /*
+ * SHUTDOWN resent from the server, move to SHUTDOWN_SER
+ */
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ /*
+ * client sent SHUDTDOWN_ACK, this is what we are expecting, let's
+ * move to SHUT_ACK_CLI
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ /*
+ * Server sent SHUTDOWN ACK, this should not happen, let's close the
+ * connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ /*
+ * SHUTDOWN COM from client, this should not happen, let's close the
+ * connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+
+ /*
+ * State : IP_VS_SCTP_S_SHUT_ACK_CLI
+ * SHUTDOWN ACK from the client, awaiting for SHUTDOWN COM from server
+ */
+ /*
+ * We recieved the data chuck, keep the state unchanged. I assume
+ * that still data chuncks can be received by both the peers in
+ * SHUDOWN state
+ */
+
+ {{IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
+ * âIf an INIT ACK is received by an endpoint in any state other
+ * than the COOKIE-WAIT state, the endpoint should discard the
+ * INIT ACK chunkâ. Stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
+ * peer and peer shall move to the ESTABISHED. if it doesn't handle
+ * it will send ERROR chunk. So, stay in the same state
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, not sure what to do stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ /*
+ * SHUTDOWN sent from the client, move to SHUDDOWN_CLI
+ */
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ /*
+ * SHUTDOWN sent from the server, move to SHUTDOWN_SER
+ */
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ /*
+ * client resent SHUDTDOWN_ACK, let's stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ /*
+ * Server sent SHUTDOWN ACK, this should not happen, let's close the
+ * connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ /*
+ * SHUTDOWN COM from client, this should not happen, let's close the
+ * connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ /*
+ * SHUTDOWN COMPLETE from server this is what we are expecting.
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+
+ /*
+ * State : IP_VS_SCTP_S_SHUT_ACK_SER
+ * SHUTDOWN ACK from the server, awaiting for SHUTDOWN COM from client
+ */
+ /*
+ * We recieved the data chuck, keep the state unchanged. I assume
+ * that still data chuncks can be received by both the peers in
+ * SHUDOWN state
+ */
+
+ {{IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_DATA_SER */ },
+ /*
+ * We have got an INIT from client. From the spec.âUpon receipt of
+ * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
+ * an INIT ACK using the same parameters it sent in its original
+ * INIT chunk (including its Initiate Tag, unchangedâ).
+ */
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ /*
+ * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
+ * âIf an INIT ACK is received by an endpoint in any state other
+ * than the COOKIE-WAIT state, the endpoint should discard the
+ * INIT ACK chunkâ. Stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ /*
+ * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
+ * peer and peer shall move to the ESTABISHED. if it doesn't handle
+ * it will send ERROR chunk. So, stay in the same state
+ */
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ /*
+ * COOKIE ACK from client, not sure what to do stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ /*
+ * SHUTDOWN sent from the client, move to SHUDDOWN_CLI
+ */
+ {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ /*
+ * SHUTDOWN sent from the server, move to SHUTDOWN_SER
+ */
+ {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ /*
+ * client sent SHUDTDOWN_ACK, this should not happen let's close
+ * the connection.
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ /*
+ * Server resent SHUTDOWN ACK, stay in the same state
+ */
+ {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ /*
+ * SHUTDOWN COM from client, this what we are expecting, let's close
+ * the connection
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ /*
+ * SHUTDOWN COMPLETE from server this should not happen.
+ */
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ },
+ /*
+ * State : IP_VS_SCTP_S_CLOSED
+ */
+ {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
+ {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
+ {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
+ {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
+ }
+};
+
+/*
+ * Timeout table[state]
+ */
+static int sctp_timeouts[IP_VS_SCTP_S_LAST + 1] = {
+ [IP_VS_SCTP_S_NONE] = 2 * HZ,
+ [IP_VS_SCTP_S_INIT_CLI] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_INIT_SER] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_INIT_ACK_CLI] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_INIT_ACK_SER] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_ECHO_CLI] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_ECHO_SER] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_ESTABLISHED] = 15 * 60 * HZ,
+ [IP_VS_SCTP_S_SHUT_CLI] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_SHUT_SER] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_SHUT_ACK_CLI] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_SHUT_ACK_SER] = 1 * 60 * HZ,
+ [IP_VS_SCTP_S_CLOSED] = 10 * HZ,
+ [IP_VS_SCTP_S_LAST] = 2 * HZ,
+};
+
+static const char *sctp_state_name_table[IP_VS_SCTP_S_LAST + 1] = {
+ [IP_VS_SCTP_S_NONE] = "NONE",
+ [IP_VS_SCTP_S_INIT_CLI] = "INIT_CLI",
+ [IP_VS_SCTP_S_INIT_SER] = "INIT_SER",
+ [IP_VS_SCTP_S_INIT_ACK_CLI] = "INIT_ACK_CLI",
+ [IP_VS_SCTP_S_INIT_ACK_SER] = "INIT_ACK_SER",
+ [IP_VS_SCTP_S_ECHO_CLI] = "COOKIE_ECHO_CLI",
+ [IP_VS_SCTP_S_ECHO_SER] = "COOKIE_ECHO_SER",
+ [IP_VS_SCTP_S_ESTABLISHED] = "ESTABISHED",
+ [IP_VS_SCTP_S_SHUT_CLI] = "SHUTDOWN_CLI",
+ [IP_VS_SCTP_S_SHUT_SER] = "SHUTDOWN_SER",
+ [IP_VS_SCTP_S_SHUT_ACK_CLI] = "SHUTDOWN_ACK_CLI",
+ [IP_VS_SCTP_S_SHUT_ACK_SER] = "SHUTDOWN_ACK_SER",
+ [IP_VS_SCTP_S_CLOSED] = "CLOSED",
+ [IP_VS_SCTP_S_LAST] = "BUG!"
+};
+
+
+static const char *sctp_state_name(int state)
+{
+ if (state >= IP_VS_SCTP_S_LAST)
+ return "ERR!";
+ if (sctp_state_name_table[state])
+ return sctp_state_name_table[state];
+ return "?";
+}
+
+static void sctp_timeout_change(struct ip_vs_protocol *pp, int flags)
+{
+}
+
+static int
+sctp_set_state_timeout(struct ip_vs_protocol *pp, char *sname, int to)
+{
+
+return ip_vs_set_state_timeout(pp->timeout_table, IP_VS_SCTP_S_LAST,
+ sctp_state_name_table, sname, to);
+}
+
+static inline int
+set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp,
+ int direction, const struct sk_buff *skb)
+{
+ sctp_chunkhdr_t _sctpch, *sch;
+ unsigned char chunk_type;
+ int event, next_state;
+ int ihl;
+
+#ifdef CONFIG_IP_VS_IPV6
+ ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
+#else
+ ihl = ip_hdrlen(skb);
+#endif
+
+ sch = skb_header_pointer(skb, ihl + sizeof(sctp_sctphdr_t),
+ sizeof(_sctpch), &_sctpch);
+ if (sch == NULL)
+ return 0;
+
+ chunk_type = sch->type;
+ /*
+ * Section 3: Multiple chunks can be bundled into one SCTP packet
+ * up to the MTU size, except for the INIT, INIT ACK, and
+ * SHUTDOWN COMPLETE chunks. These chunks MUST NOT be bundled with
+ * any other chunk in a packet.
+ *
+ * Section 3.3.7: DATA chunks MUST NOT be bundled with ABORT. Control
+ * chunks (except for INIT, INIT ACK, and SHUTDOWN COMPLETE) MAY be
+ * bundled with an ABORT, but they MUST be placed before the ABORT
+ * in the SCTP packet or they will be ignored by the receiver.
+ */
+ if ((sch->type == SCTP_CID_COOKIE_ECHO) ||
+ (sch->type == SCTP_CID_COOKIE_ACK)) {
+ sch = skb_header_pointer(skb, (ihl + sizeof(sctp_sctphdr_t) +
+ sch->length), sizeof(_sctpch), &_sctpch);
+ if (sch) {
+ if (sch->type == SCTP_CID_ABORT)
+ chunk_type = sch->type;
+ }
+ }
+
+ event = sctp_events[chunk_type];
+
+ /*
+ * If the direction is IP_VS_DIR_OUTPUT, this event is from server
+ */
+ if (direction == IP_VS_DIR_OUTPUT)
+ event++;
+ /*
+ * get next state
+ */
+ next_state = sctp_states_table[cp->state][event].next_state;
+
+ if (next_state != cp->state) {
+ struct ip_vs_dest *dest = cp->dest;
+
+ IP_VS_DBG_BUF(8, "%s %s %s:%d->"
+ "%s:%d state: %s->%s conn->refcnt:%d\n",
+ pp->name,
+ ((direction == IP_VS_DIR_OUTPUT) ?
+ "output " : "input "),
+ IP_VS_DBG_ADDR(cp->af, &cp->daddr),
+ ntohs(cp->dport),
+ IP_VS_DBG_ADDR(cp->af, &cp->caddr),
+ ntohs(cp->cport),
+ sctp_state_name(cp->state),
+ sctp_state_name(next_state),
+ atomic_read(&cp->refcnt));
+ if (dest) {
+ if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
+ (next_state != IP_VS_SCTP_S_ESTABLISHED)) {
+ atomic_dec(&dest->activeconns);
+ atomic_inc(&dest->inactconns);
+ cp->flags |= IP_VS_CONN_F_INACTIVE;
+ } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) &&
+ (next_state == IP_VS_SCTP_S_ESTABLISHED)) {
+ atomic_inc(&dest->activeconns);
+ atomic_dec(&dest->inactconns);
+ cp->flags &= ~IP_VS_CONN_F_INACTIVE;
+ }
+ }
+ }
+
+ cp->timeout = pp->timeout_table[cp->state = next_state];
+
+ return 1;
+}
+
+static int
+sctp_state_transition(struct ip_vs_conn *cp, int direction,
+ const struct sk_buff *skb, struct ip_vs_protocol *pp)
+{
+ int ret = 0;
+
+ spin_lock(&cp->lock);
+ ret = set_sctp_state(pp, cp, direction, skb);
+ spin_unlock(&cp->lock);
+
+ return ret;
+}
+
+/*
+ * Hash table for SCTP application incarnations
+ */
+#define SCTP_APP_TAB_BITS 4
+#define SCTP_APP_TAB_SIZE (1 << SCTP_APP_TAB_BITS)
+#define SCTP_APP_TAB_MASK (SCTP_APP_TAB_SIZE - 1)
+
+static struct list_head sctp_apps[SCTP_APP_TAB_SIZE];
+static DEFINE_SPINLOCK(sctp_app_lock);
+
+static inline __u16 sctp_app_hashkey(__be16 port)
+{
+ return (((__force u16)port >> SCTP_APP_TAB_BITS) ^ (__force u16)port)
+ & SCTP_APP_TAB_MASK;
+}
+
+static int sctp_register_app(struct ip_vs_app *inc)
+{
+ struct ip_vs_app *i;
+ __u16 hash;
+ __be16 port = inc->port;
+ int ret = 0;
+
+ hash = sctp_app_hashkey(port);
+
+ spin_lock_bh(&sctp_app_lock);
+ list_for_each_entry(i, &sctp_apps[hash], p_list) {
+ if (i->port == port) {
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+ list_add(&inc->p_list, &sctp_apps[hash]);
+ atomic_inc(&ip_vs_protocol_sctp.appcnt);
+out:
+ spin_unlock_bh(&sctp_app_lock);
+
+ return ret;
+}
+
+static void sctp_unregister_app(struct ip_vs_app *inc)
+{
+ spin_lock_bh(&sctp_app_lock);
+ atomic_dec(&ip_vs_protocol_sctp.appcnt);
+ list_del(&inc->p_list);
+ spin_unlock_bh(&sctp_app_lock);
+}
+
+static int sctp_app_conn_bind(struct ip_vs_conn *cp)
+{
+ int hash;
+ struct ip_vs_app *inc;
+ int result = 0;
+
+ /* Default binding: bind app only for NAT */
+ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
+ return 0;
+ /* Lookup application incarnations and bind the right one */
+ hash = sctp_app_hashkey(cp->vport);
+
+ spin_lock(&sctp_app_lock);
+ list_for_each_entry(inc, &sctp_apps[hash], p_list) {
+ if (inc->port == cp->vport) {
+ if (unlikely(!ip_vs_app_inc_get(inc)))
+ break;
+ spin_unlock(&sctp_app_lock);
+
+ IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->"
+ "%s:%u to app %s on port %u\n",
+ __func__,
+ IP_VS_DBG_ADDR(cp->af, &cp->caddr),
+ ntohs(cp->cport),
+ IP_VS_DBG_ADDR(cp->af, &cp->vaddr),
+ ntohs(cp->vport),
+ inc->name, ntohs(inc->port));
+ cp->app = inc;
+ if (inc->init_conn)
+ result = inc->init_conn(inc, cp);
+ goto out;
+ }
+ }
+ spin_unlock(&sctp_app_lock);
+out:
+ return result;
+}
+
+static void ip_vs_sctp_init(struct ip_vs_protocol *pp)
+{
+ IP_VS_INIT_HASH_TABLE(sctp_apps);
+ pp->timeout_table = sctp_timeouts;
+}
+
+
+static void ip_vs_sctp_exit(struct ip_vs_protocol *pp)
+{
+
+}
+
+struct ip_vs_protocol ip_vs_protocol_sctp = {
+ .name = "SCTP",
+ .protocol = IPPROTO_SCTP,
+ .num_states = IP_VS_SCTP_S_LAST,
+ .dont_defrag = 0,
+ .appcnt = ATOMIC_INIT(0),
+ .init = ip_vs_sctp_init,
+ .exit = ip_vs_sctp_exit,
+ .register_app = sctp_register_app,
+ .unregister_app = sctp_unregister_app,
+ .conn_schedule = sctp_conn_schedule,
+ .conn_in_get = sctp_conn_in_get,
+ .conn_out_get = sctp_conn_out_get,
+ .snat_handler = sctp_snat_handler,
+ .dnat_handler = sctp_dnat_handler,
+ .csum_check = sctp_csum_check,
+ .state_name = sctp_state_name,
+ .state_transition = sctp_state_transition,
+ .app_conn_bind = sctp_app_conn_bind,
+ .debug_packet = ip_vs_tcpudp_debug_packet,
+ .timeout_change = sctp_timeout_change,
+ .set_state_timeout = sctp_set_state_timeout,
+};
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index e177f0d..8fb0ae6 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -400,6 +400,11 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
flags |= IP_VS_CONN_F_INACTIVE;
else
flags &= ~IP_VS_CONN_F_INACTIVE;
+ } else if (s->protocol == IPPROTO_SCTP) {
+ if (state != IP_VS_SCTP_S_ESTABLISHED)
+ flags |= IP_VS_CONN_F_INACTIVE;
+ else
+ flags &= ~IP_VS_CONN_F_INACTIVE;
}
cp = ip_vs_conn_new(AF_INET, s->protocol,
(union nf_inet_addr *)&s->caddr,
@@ -434,6 +439,15 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
atomic_dec(&dest->inactconns);
cp->flags &= ~IP_VS_CONN_F_INACTIVE;
}
+ } else if ((cp->dest) && (cp->protocol == IPPROTO_SCTP) &&
+ (cp->state != state)) {
+ dest = cp->dest;
+ if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
+ (state != IP_VS_SCTP_S_ESTABLISHED)) {
+ atomic_dec(&dest->activeconns);
+ atomic_inc(&dest->inactconns);
+ cp->flags &= ~IP_VS_CONN_F_INACTIVE;
+ }
}

if (opt)

On Tue, Feb 16, 2010 at 04:13:55PM +1100, Simon Horman wrote:
> On Fri, Feb 12, 2010 at 05:53:04PM +0530, Mohan Reddy wrote:
> > Hi Simon&Wensong,
> >
> > Please some review this code and let me know your comments. I am re-posting this
> > as there is no response from any one. You are welcome for any type of comments.
> >
> > This patch enhances IPVS to load balance SCTP transport protocol packets. This
> > is done based on the SCTP rfc 4960. All possible control chunks have been
> > taken care. The state machine used in this code looks some what lengthy. I
> > tried to make the state machine easy to understand.
> >
> > This patch is against nf-next.2.6 tree.
> >
> > Thanks,
> > Mohan Reddy
>
> Hi Mohan,
>
> sorry once again for the very long delay in reviewing your patch.
>
> I'm not an export on SCTP but the patch seems quite clean to me
> and looks unlikely to break the existing LVS code. So I think
> it is worth getting upstream so people can test it. By which I mean,
> this looks like a good candidate for 2.6.35.
>
> I have made some comments in-line, almost exclusively style issues.
>
> > Signed-off-by: Venkata Mohan Reddy Koppula <mohanreddykv@xxxxxxxxx>
> >
> >
> > diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
> > index a816c37..fe82b1e 100644
> > --- a/include/net/ip_vs.h
> > +++ b/include/net/ip_vs.h
> > @@ -225,6 +225,26 @@ enum {
> > };
> >
> > /*
> > + * SCTP State Values
> > + */
> > +enum ip_vs_sctp_states {
> > + IP_VS_SCTP_S_NONE,
> > + IP_VS_SCTP_S_INIT_CLI,
> > + IP_VS_SCTP_S_INIT_SER,
> > + IP_VS_SCTP_S_INIT_ACK_CLI,
> > + IP_VS_SCTP_S_INIT_ACK_SER,
> > + IP_VS_SCTP_S_ECHO_CLI,
> > + IP_VS_SCTP_S_ECHO_SER,
> > + IP_VS_SCTP_S_ESTABLISHED,
> > + IP_VS_SCTP_S_SHUT_CLI,
> > + IP_VS_SCTP_S_SHUT_SER,
> > + IP_VS_SCTP_S_SHUT_ACK_CLI,
> > + IP_VS_SCTP_S_SHUT_ACK_SER,
> > + IP_VS_SCTP_S_CLOSED,
> > + IP_VS_SCTP_S_LAST
> > +};
> > +
> > +/*
> > * Delta sequence info structure
> > * Each ip_vs_conn has 2 (output AND input seq. changes).
> > * Only used in the VS/NAT.
> > @@ -741,7 +761,7 @@ extern struct ip_vs_protocol ip_vs_protocol_udp;
> > extern struct ip_vs_protocol ip_vs_protocol_icmp;
> > extern struct ip_vs_protocol ip_vs_protocol_esp;
> > extern struct ip_vs_protocol ip_vs_protocol_ah;
> > -
> > +extern struct ip_vs_protocol ip_vs_protocol_sctp;
> >
> > /*
> > * Registering/unregistering scheduler functions
> > diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig
> > index c71e543..6dba1e8 100644
> > --- a/net/netfilter/ipvs/Kconfig
> > +++ b/net/netfilter/ipvs/Kconfig
> > @@ -104,6 +104,13 @@ config IP_VS_PROTO_AH
> > This option enables support for load balancing AH (Authentication
> > Header) transport protocol. Say Y if unsure.
> >
> > +config IP_VS_PROTO_SCTP
> > + bool "SCTP load balancing support"
> > + select LIBCRC32C
> > + ---help---
> > + This option enables support for load balancing SCTP transport
> > + protocol. Say Y if unsure.
> > +
>
> I wonder if this should be marked as EXPERIMENTAL
>
> > comment "IPVS scheduler"
> >
> > config IP_VS_RR
> > diff --git a/net/netfilter/ipvs/Makefile b/net/netfilter/ipvs/Makefile
> > index 73a46fe..e3baefd 100644
> > --- a/net/netfilter/ipvs/Makefile
> > +++ b/net/netfilter/ipvs/Makefile
> > @@ -7,6 +7,7 @@ ip_vs_proto-objs-y :=
> > ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_TCP) += ip_vs_proto_tcp.o
> > ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_UDP) += ip_vs_proto_udp.o
> > ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_AH_ESP) += ip_vs_proto_ah_esp.o
> > +ip_vs_proto-objs-$(CONFIG_IP_VS_PROTO_SCTP) += ip_vs_proto_sctp.o
> >
> > ip_vs-objs := ip_vs_conn.o ip_vs_core.o ip_vs_ctl.o ip_vs_sched.o \
> > ip_vs_xmit.o ip_vs_app.o ip_vs_sync.o \
> > diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
> > index 847ffca..dae3af0 100644
> > --- a/net/netfilter/ipvs/ip_vs_core.c
> > +++ b/net/netfilter/ipvs/ip_vs_core.c
> > @@ -31,6 +31,7 @@
> > #include <linux/kernel.h>
> > #include <linux/ip.h>
> > #include <linux/tcp.h>
> > +#include <linux/sctp.h>
> > #include <linux/icmp.h>
> >
> > #include <net/ip.h>
> > @@ -81,6 +82,8 @@ const char *ip_vs_proto_name(unsigned proto)
> > return "UDP";
> > case IPPROTO_TCP:
> > return "TCP";
> > + case IPPROTO_SCTP:
> > + return "SCTP";
> > case IPPROTO_ICMP:
> > return "ICMP";
> > #ifdef CONFIG_IP_VS_IPV6
> > @@ -589,8 +592,9 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
> > ip_send_check(ciph);
> > }
> >
> > - /* the TCP/UDP port */
> > - if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol) {
> > + /* the TCP/UDP/SCTP port */
> > + if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
> > + IPPROTO_SCTP == ciph->protocol) {
>
> Minor style issue, but I believe that the preferred indentation is
> to align the start of continuing lines with the inside of the
> preceding '(' bracket.
>
> if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
> IPPROTO_SCTP == ciph->protocol) {
>
> Could you fix this up throughout your patch?
>
> In particular, I notice some changes only change indentation.
> Please don't do that as it just adds noise to the change.
>
> > __be16 *ports = (void *)ciph + ciph->ihl*4;
> >
> > if (inout)
> > @@ -630,8 +634,9 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
> > ciph->saddr = cp->daddr.in6;
> > }
> >
> > - /* the TCP/UDP port */
> > - if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr) {
> > + /* the TCP/UDP/SCTP port */
> > + if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr ||
> > + IPPROTO_SCTP == ciph->nexthdr) {
> > __be16 *ports = (void *)ciph + sizeof(struct ipv6hdr);
> >
> > if (inout)
> > @@ -679,7 +684,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
> > goto out;
> > }
> >
> > - if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol)
> > + if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
> > + IPPROTO_SCTP == protocol)
> > offset += 2 * sizeof(__u16);
> > if (!skb_make_writable(skb, offset))
> > goto out;
> > @@ -857,6 +863,21 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related)
> > }
> > #endif
> >
> > +/*
> > + * Check if sctp chunc is ABORT chunk
> > + */
> > +static inline int is_sctp_abort(const struct sk_buff *skb, int nh_len)
> > +{
> > + sctp_chunkhdr_t *sch, schunk;
> > + sch = skb_header_pointer(skb, nh_len + sizeof(sctp_sctphdr_t),
> > + sizeof(schunk), &schunk);
> > + if (sch == NULL)
> > + return 0;
> > + if (sch->type == SCTP_CID_ABORT)
> > + return 1;
> > + return 0;
> > +}
> > +
> > static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len)
> > {
> > struct tcphdr _tcph, *th;
> > @@ -998,24 +1019,30 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
> >
> > if (unlikely(!cp)) {
> > if (sysctl_ip_vs_nat_icmp_send &&
> > - (pp->protocol == IPPROTO_TCP ||
> > - pp->protocol == IPPROTO_UDP)) {
> > + (pp->protocol == IPPROTO_TCP ||
> > + pp->protocol == IPPROTO_UDP ||
> > + pp->protocol == IPPROTO_SCTP)) {
> > __be16 _ports[2], *pptr;
> >
> > pptr = skb_header_pointer(skb, iph.len,
> > - sizeof(_ports), _ports);
> > + sizeof(_ports), _ports);
> > if (pptr == NULL)
> > return NF_ACCEPT; /* Not for me */
> > if (ip_vs_lookup_real_service(af, iph.protocol,
> > - &iph.saddr,
> > - pptr[0])) {
> > + &iph.saddr,
> > + pptr[0])) {
> > /*
> > * Notify the real server: there is no
> > * existing entry if it is not RST
> > * packet or not TCP packet.
> > */
> > - if (iph.protocol != IPPROTO_TCP
> > - || !is_tcp_reset(skb, iph.len)) {
> > + if ((iph.protocol != IPPROTO_TCP &&
> > + iph.protocol != IPPROTO_SCTP)
> > + || ((iph.protocol == IPPROTO_TCP
> > + && !is_tcp_reset(skb, iph.len))
> > + || (iph.protocol == IPPROTO_SCTP
> > + && !is_sctp_abort(skb,
> > + iph.len)))) {
> > #ifdef CONFIG_IP_VS_IPV6
> > if (af == AF_INET6)
> > icmpv6_send(skb,
> > @@ -1235,7 +1262,8 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
> >
> > /* do the statistics and put it back */
> > ip_vs_in_stats(cp, skb);
> > - if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr)
> > + if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr ||
> > + IPPROTO_SCTP == cih->nexthdr)
> > offset += 2 * sizeof(__u16);
> > verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset);
> > /* do not touch skb anymore */
> > @@ -1358,6 +1386,21 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
> > * encorage the standby servers to update the connections timeout
> > */
> > pkts = atomic_add_return(1, &cp->in_pkts);
> > + if (af == AF_INET && (ip_vs_sync_state & IP_VS_STATE_MASTER) &&
> > + cp->protocol == IPPROTO_SCTP) {
> > + if ((cp->state == IP_VS_SCTP_S_ESTABLISHED &&
> > + (atomic_read(&cp->in_pkts) %
> > + sysctl_ip_vs_sync_threshold[1]
> > + == sysctl_ip_vs_sync_threshold[0])) ||
> > + (cp->old_state != cp->state &&
> > + ((cp->state == IP_VS_SCTP_S_CLOSED) ||
> > + (cp->state == IP_VS_SCTP_S_SHUT_ACK_CLI) ||
> > + (cp->state == IP_VS_SCTP_S_SHUT_ACK_SER)))) {
> > + ip_vs_sync_conn(cp);
> > + goto out;
> > + }
> > + }
> > +
>
> Having a separate block for the synchronisation of SCTP seems
> a bit messy. But thats mainly due to the state of the existing code.
> So I think its fine.
>
> > if (af == AF_INET &&
> > (ip_vs_sync_state & IP_VS_STATE_MASTER) &&
> > (((cp->protocol != IPPROTO_TCP ||
> > @@ -1370,6 +1413,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
> > (cp->state == IP_VS_TCP_S_CLOSE_WAIT) ||
> > (cp->state == IP_VS_TCP_S_TIME_WAIT)))))
> > ip_vs_sync_conn(cp);
> > +out:
> > cp->old_state = cp->state;
> >
> > ip_vs_conn_put(cp);
> > diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
> > index 93420ea..564a886 100644
> > --- a/net/netfilter/ipvs/ip_vs_ctl.c
> > +++ b/net/netfilter/ipvs/ip_vs_ctl.c
> > @@ -2128,8 +2128,9 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
> > }
> > }
> >
> > - /* Check for valid protocol: TCP or UDP, even for fwmark!=0 */
> > - if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP) {
> > + /* Check for valid protocol: TCP or UDP or SCTP, even for fwmark!=0 */
> > + if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP &&
> > + usvc.protocol != IPPROTO_SCTP) {
> > pr_err("set_ctl: invalid protocol: %d %pI4:%d %s\n",
> > usvc.protocol, &usvc.addr.ip,
> > ntohs(usvc.port), usvc.sched_name);
> > diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c
> > index 3e76716..0e58455 100644
> > --- a/net/netfilter/ipvs/ip_vs_proto.c
> > +++ b/net/netfilter/ipvs/ip_vs_proto.c
> > @@ -257,6 +257,9 @@ int __init ip_vs_protocol_init(void)
> > #ifdef CONFIG_IP_VS_PROTO_UDP
> > REGISTER_PROTOCOL(&ip_vs_protocol_udp);
> > #endif
> > +#ifdef CONFIG_IP_VS_PROTO_SCTP
> > + REGISTER_PROTOCOL(&ip_vs_protocol_sctp);
> > +#endif
> > #ifdef CONFIG_IP_VS_PROTO_AH
> > REGISTER_PROTOCOL(&ip_vs_protocol_ah);
> > #endif
> > diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
> > new file mode 100644
> > index 0000000..4303ec5
> > --- /dev/null
> > +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
> > @@ -0,0 +1,1190 @@
> > +#include <linux/kernel.h>
> > +#include <linux/ip.h>
> > +#include <linux/sctp.h>
> > +#include <net/ip.h>
> > +#include <net/ip6_checksum.h>
> > +#include <linux/netfilter.h>
> > +#include <linux/netfilter_ipv4.h>
> > +#include <net/sctp/checksum.h>
> > +#include <net/ip_vs.h>
> > +
> > +
> > +static struct ip_vs_conn *sctp_conn_in_get(int af,
> > + const struct sk_buff *skb,
> > + struct ip_vs_protocol *pp,
> > + const struct ip_vs_iphdr *iph,
> > + unsigned int proto_off,
> > + int inverse)
>
> Again, indentation.
>
> > +{
> > + __be16 _ports[2], *pptr;
> > +
> > + pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
> > + if (pptr == NULL)
> > + return NULL;
> > +
> > + if (likely(!inverse)) {
> > + return ip_vs_conn_in_get(af, iph->protocol,
> > + &iph->saddr, pptr[0],
> > + &iph->daddr, pptr[1]);
> > + } else {
> > + return ip_vs_conn_in_get(af, iph->protocol,
> > + &iph->daddr, pptr[1],
> > + &iph->saddr, pptr[0]);
> > + }
>
> There is no need for { } to enclose a single statement.
> I would perfer:
>
> if (likely(!inverse))
> return ip_vs_conn_in_get(af, iph->protocol,
> &iph->saddr, pptr[0],
> &iph->daddr, pptr[1]);
> else
> return ip_vs_conn_in_get(af, iph->protocol,
> &iph->daddr, pptr[1],
> &iph->saddr, pptr[0]);
>
> > +}
> > +
> > +static struct ip_vs_conn *sctp_conn_out_get(int af,
> > + const struct sk_buff *skb,
> > + struct ip_vs_protocol *pp,
> > + const struct ip_vs_iphdr *iph,
> > + unsigned int proto_off,
> > + int inverse)
> > +{
> > + __be16 _ports[2], *pptr;
> > +
> > + pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
> > + if (pptr == NULL)
> > + return NULL;
> > +
> > + if (likely(!inverse)) {
> > + return ip_vs_conn_out_get(af, iph->protocol,
> > + &iph->saddr, pptr[0],
> > + &iph->daddr, pptr[1]);
> > + } else {
> > + return ip_vs_conn_out_get(af, iph->protocol,
> > + &iph->daddr, pptr[1],
> > + &iph->saddr, pptr[0]);
> > + }
>
> Again, no need for { }
>
> > +}
> > +
> > +static int
> > +sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
> > + int *verdict, struct ip_vs_conn **cpp)
> > +{
> > + struct ip_vs_service *svc;
> > + sctp_chunkhdr_t _schunkh, *sch;
> > + sctp_sctphdr_t *sh, _sctph;
> > + struct ip_vs_iphdr iph;
> > +
> > + ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
> > +
> > + sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph);
> > + if (sh == NULL)
> > + return 0;
> > +
> > +
>
> Please only use at most one blank line.
>
> > + sch = skb_header_pointer(skb, iph.len + sizeof(sctp_sctphdr_t),
> > + sizeof(_schunkh), &_schunkh);
> > + if (sch == NULL)
> > + return 0;
> > +
> > + if ((sch->type == SCTP_CID_INIT) &&
> > + (svc = ip_vs_service_get(af, skb->mark, iph.protocol,
> > + &iph.daddr, sh->dest))) {
> > + if (ip_vs_todrop()) {
> > + /*
> > + * It seems that we are very loaded.
> > + * We have to drop this packet :(
> > + */
> > + ip_vs_service_put(svc);
> > + *verdict = NF_DROP;
> > + return 0;
> > + }
> > + /*
> > + * Let the virtual server select a real server for the
> > + * incoming connection, and create a connection entry.
> > + */
> > + *cpp = ip_vs_schedule(svc, skb);
> > + if (!*cpp) {
> > + *verdict = ip_vs_leave(svc, skb, pp);
> > + return 0;
> > + }
> > + ip_vs_service_put(svc);
> > + }
> > +
> > + return 1;
> > +}
> > +
> > +static int
> > +sctp_snat_handler(struct sk_buff *skb,
> > + struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
> > +{
> > + sctp_sctphdr_t *sctph;
> > + unsigned int sctphoff;
> > + __be32 crc32;
> > +
> > +#ifdef CONFIG_IP_VS_IPV6
> > + if (cp->af == AF_INET6)
> > + sctphoff = sizeof(struct ipv6hdr);
> > + else
> > +#endif
> > + sctphoff = ip_hdrlen(skb);
> > +
> > + /* csum_check requires unshared skb */
> > + if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
> > + return 0;
> > +
> > + if (unlikely(cp->app != NULL)) {
> > + /* Some checks before mangling */
> > + if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
> > + return 0;
> > +
> > + /* Call application helper if needed */
> > + if (!ip_vs_app_pkt_out(cp, skb))
> > + return 0;
> > + }
> > +
> > + sctph = (void *) skb_network_header(skb) + sctphoff;
> > + sctph->source = cp->vport;
> > +
> > + /* Calculate the checksum */
> > + crc32 =
> > + sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff);
> > + for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next)
> > + crc32 =
> > + sctp_update_cksum((u8 *) skb->data, skb_headlen(skb),
> > + crc32);
>
> Line wrapping: I think the following is preferred (x2)
>
> /* Calculate the checksum */
> crc32 = sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff);
> for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next)
> crc32 = sctp_update_cksum((u8 *) skb->data, skb_headlen(skb),
> crc32);
>
> > + crc32 = sctp_end_cksum(crc32);
> > + sctph->checksum = crc32;
> > +
> > + return 1;
> > +}
> > +
> > +static int
> > +sctp_dnat_handler(struct sk_buff *skb,
> > + struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
> > +{
> > +
> > + sctp_sctphdr_t *sctph;
> > + unsigned int sctphoff;
> > + __be32 crc32;
> > +
> > +#ifdef CONFIG_IP_VS_IPV6
> > + if (cp->af == AF_INET6)
> > + sctphoff = sizeof(struct ipv6hdr);
> > + else
> > +#endif
> > + sctphoff = ip_hdrlen(skb);
> > +
> > + /* csum_check requires unshared skb */
> > + if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
> > + return 0;
> > +
> > + if (unlikely(cp->app != NULL)) {
> > + /* Some checks before mangling */
> > + if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
> > + return 0;
> > +
> > + /* Call application helper if needed */
> > + if (!ip_vs_app_pkt_out(cp, skb))
> > + return 0;
> > + }
> > +
> > + sctph = (void *) skb_network_header(skb) + sctphoff;
> > + sctph->dest = cp->dport;
> > +
> > + /* Calculate the checksum */
> > + crc32 =
> > + sctp_start_cksum((u8 *) sctph, skb_headlen(skb) - sctphoff);
> > + for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next)
> > + crc32 =
> > + sctp_update_cksum((u8 *) skb->data, skb_headlen(skb),
> > + crc32);
>
> Line wrapping again.
>
> The checksum calculation seems the same as in +sctp_snat_handler().
> Perhaps this could be consolidated?
>
> > + crc32 = sctp_end_cksum(crc32);
> > + sctph->checksum = crc32;
> > +
> > + return 1;
> > +}
> > +
> > +static int
> > +sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
> > +{
> > + struct sk_buff *list = skb_shinfo(skb)->frag_list;
> > + unsigned int sctphoff;
> > + struct sctphdr *sh, _sctph;
> > + __le32 cmp;
> > + __le32 val;
> > + __u32 tmp;
> > +
> > +#ifdef CONFIG_IP_VS_IPV6
> > + if (af == AF_INET6)
> > + sctphoff = sizeof(struct ipv6hdr);
> > + else
> > +#endif
> > + sctphoff = ip_hdrlen(skb);
> > +
> > + sh = skb_header_pointer(skb, sctphoff, sizeof(_sctph), &_sctph);
> > + if (sh == NULL)
> > + return 0;
> > +
> > + cmp = sh->checksum;
> > +
> > + tmp = sctp_start_cksum((__u8 *) sh, skb_headlen(skb));
> > + for (; list; list = list->next)
> > + tmp =
> > + sctp_update_cksum((__u8 *) list->data,
> > + skb_headlen(list), tmp);
> > +
> > + val = sctp_end_cksum(tmp);
> > +
> > + if (val != cmp) {
> > + /* CRC failure, dump it. */
> > + IP_VS_DBG_RL_PKT(0, pp, skb, 0,
> > + "Failed checksum for");
> > + return 0;
> > + }
> > + return 1;
> > +}
> > +
> > +struct ipvs_sctp_nextstate {
> > + int next_state;
> > +};
> > +enum ipvs_sctp_event_t {
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_DATA_SER,
> > + IP_VS_SCTP_EVE_INIT_CLI,
> > + IP_VS_SCTP_EVE_INIT_SER,
> > + IP_VS_SCTP_EVE_INIT_ACK_CLI,
> > + IP_VS_SCTP_EVE_INIT_ACK_SER,
> > + IP_VS_SCTP_EVE_COOKIE_ECHO_CLI,
> > + IP_VS_SCTP_EVE_COOKIE_ECHO_SER,
> > + IP_VS_SCTP_EVE_COOKIE_ACK_CLI,
> > + IP_VS_SCTP_EVE_COOKIE_ACK_SER,
> > + IP_VS_SCTP_EVE_ABORT_CLI,
> > + IP_VS_SCTP_EVE__ABORT_SER,
> > + IP_VS_SCTP_EVE_SHUT_CLI,
> > + IP_VS_SCTP_EVE_SHUT_SER,
> > + IP_VS_SCTP_EVE_SHUT_ACK_CLI,
> > + IP_VS_SCTP_EVE_SHUT_ACK_SER,
> > + IP_VS_SCTP_EVE_SHUT_COM_CLI,
> > + IP_VS_SCTP_EVE_SHUT_COM_SER,
> > + IP_VS_SCTP_EVE_LAST
> > +};
> > +
> > +static enum ipvs_sctp_event_t sctp_events[255] = {
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_INIT_CLI,
> > + IP_VS_SCTP_EVE_INIT_ACK_CLI,
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_ABORT_CLI,
> > + IP_VS_SCTP_EVE_SHUT_CLI,
> > + IP_VS_SCTP_EVE_SHUT_ACK_CLI,
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_COOKIE_ECHO_CLI,
> > + IP_VS_SCTP_EVE_COOKIE_ACK_CLI,
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_DATA_CLI,
> > + IP_VS_SCTP_EVE_SHUT_COM_CLI,
> > +};
> > +
> > +static struct ipvs_sctp_nextstate
> > + sctp_states_table[IP_VS_SCTP_S_LAST][IP_VS_SCTP_EVE_LAST] = {
> > + /*
> > + * STATE : IP_VS_SCTP_S_NONE
> > + */
> > + /*next state *//*event */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ },
> > + },
> > + /*
> > + * STATE : IP_VS_SCTP_S_INIT_CLI
> > + * Cient sent INIT and is waiting for reply from server(In ECHO_WAIT)
> > + */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_ECHO_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_INIT_SER
> > + * Server sent INIT and waiting for INIT ACK from the client
> > + */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_INIT_ACK_CLI
> > + * Client sent INIT ACK and waiting for ECHO from the server
> > + */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK has been resent by the client, let us stay is in
> > + * the same state
> > + */
> > + {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + /*
> > + * INIT_ACK sent by the server, close the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * ECHO by client, it should not happen, close the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + /*
> > + * ECHO by server, this is what we are expecting, move to ECHO_SER
> > + */
> > + {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, it should not happen, close the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + /*
> > + * Unexpected COOKIE ACK from server, staty in the same state
> > + */
> > + {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_INIT_ACK_SER
> > + * Server sent INIT ACK and waiting for ECHO from the client
> > + */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * Unexpected INIT_ACK by the client, let us close the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + /*
> > + * INIT_ACK resent by the server, let us move to same state
> > + */
> > + {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client send the ECHO, this is what we are expecting,
> > + * move to ECHO_CLI
> > + */
> > + {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + /*
> > + * ECHO received from the server, Not sure what to do,
> > + * let us close it
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, let us stay in the same state
> > + */
> > + {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + /*
> > + * COOKIE ACK from server, hmm... this should not happen, lets close
> > + * the connection.
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_ECHO_CLI
> > + * Cient sent ECHO and waiting COOKEI ACK from the Server
> > + */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK has been by the client, let us close the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + /*
> > + * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
> > + * âIf an INIT ACK is received by an endpoint in any state other
> > + * than the COOKIE-WAIT state, the endpoint should discard the
> > + * INIT ACK chunkâ. Stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client resent the ECHO, let us stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + /*
> > + * ECHO received from the server, Not sure what to do,
> > + * let us close it
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, this shoud not happen, let's close the
> > + * connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + /*
> > + * COOKIE ACK from server, this is what we are awaiting,lets move to
> > + * ESTABLISHED.
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_ECHO_SER
> > + * Server sent ECHO and waiting COOKEI ACK from the client
> > + */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
> > + * âIf an INIT ACK is received by an endpoint in any state other
> > + * than the COOKIE-WAIT state, the endpoint should discard the
> > + * INIT ACK chunkâ. Stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + /*
> > + * INIT_ACK has been by the server, let us close the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client sent the ECHO, not sure what to do, let's close the
> > + * connection.
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + /*
> > + * ECHO resent by the server, stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, this is what we are expecting, let's move
> > + * to ESTABLISHED.
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + /*
> > + * COOKIE ACK from server, this should not happen, lets close the
> > + * connection.
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_ESTABLISHED
> > + * Association established
> > + */
> > + {{IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
> > + * âIf an INIT ACK is received by an endpoint in any state other
> > + * than the COOKIE-WAIT state, the endpoint should discard the
> > + * INIT ACK chunkâ. Stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
> > + * peer and peer shall move to the ESTABISHED. if it doesn't handle
> > + * it will send ERROR chunk. So, stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, not sure what to do stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + /*
> > + * SHUTDOWN from the client, move to SHUDDOWN_CLI
> > + */
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + /*
> > + * SHUTDOWN from the server, move to SHUTDOWN_SER
> > + */
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + /*
> > + * client sent SHUDTDOWN_ACK, this should not happen, let's close
> > + * the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_SHUT_CLI
> > + * SHUTDOWN sent from the client, waitinf for SHUT ACK from the server
> > + */
> > + /*
> > + * We recieved the data chuck, keep the state unchanged. I assume
> > + * that still data chuncks can be received by both the peers in
> > + * SHUDOWN state
> > + */
> > +
> > + {{IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
> > + * âIf an INIT ACK is received by an endpoint in any state other
> > + * than the COOKIE-WAIT state, the endpoint should discard the
> > + * INIT ACK chunkâ. Stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
> > + * peer and peer shall move to the ESTABISHED. if it doesn't handle
> > + * it will send ERROR chunk. So, stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, not sure what to do stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + /*
> > + * SHUTDOWN resent from the client, move to SHUDDOWN_CLI
> > + */
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + /*
> > + * SHUTDOWN from the server, move to SHUTDOWN_SER
> > + */
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + /*
> > + * client sent SHUDTDOWN_ACK, this should not happen, let's close
> > + * the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + /*
> > + * Server sent SHUTDOWN ACK, this is what we are expecting, let's move
> > + * to SHUDOWN_ACK_SER
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + /*
> > + * SHUTDOWN COM from client, this should not happen, let's close the
> > + * connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_SHUT_SER
> > + * SHUTDOWN sent from the server, waitinf for SHUTDOWN ACK from client
> > + */
> > + /*
> > + * We recieved the data chuck, keep the state unchanged. I assume
> > + * that still data chuncks can be received by both the peers in
> > + * SHUDOWN state
> > + */
> > +
> > + {{IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
> > + * âIf an INIT ACK is received by an endpoint in any state other
> > + * than the COOKIE-WAIT state, the endpoint should discard the
> > + * INIT ACK chunkâ. Stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
> > + * peer and peer shall move to the ESTABISHED. if it doesn't handle
> > + * it will send ERROR chunk. So, stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, not sure what to do stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + /*
> > + * SHUTDOWN resent from the client, move to SHUDDOWN_CLI
> > + */
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + /*
> > + * SHUTDOWN resent from the server, move to SHUTDOWN_SER
> > + */
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + /*
> > + * client sent SHUDTDOWN_ACK, this is what we are expecting, let's
> > + * move to SHUT_ACK_CLI
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + /*
> > + * Server sent SHUTDOWN ACK, this should not happen, let's close the
> > + * connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + /*
> > + * SHUTDOWN COM from client, this should not happen, let's close the
> > + * connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > +
> > + /*
> > + * State : IP_VS_SCTP_S_SHUT_ACK_CLI
> > + * SHUTDOWN ACK from the client, awaiting for SHUTDOWN COM from server
> > + */
> > + /*
> > + * We recieved the data chuck, keep the state unchanged. I assume
> > + * that still data chuncks can be received by both the peers in
> > + * SHUDOWN state
> > + */
> > +
> > + {{IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
> > + * âIf an INIT ACK is received by an endpoint in any state other
> > + * than the COOKIE-WAIT state, the endpoint should discard the
> > + * INIT ACK chunkâ. Stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
> > + * peer and peer shall move to the ESTABISHED. if it doesn't handle
> > + * it will send ERROR chunk. So, stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, not sure what to do stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + /*
> > + * SHUTDOWN sent from the client, move to SHUDDOWN_CLI
> > + */
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + /*
> > + * SHUTDOWN sent from the server, move to SHUTDOWN_SER
> > + */
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + /*
> > + * client resent SHUDTDOWN_ACK, let's stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + /*
> > + * Server sent SHUTDOWN ACK, this should not happen, let's close the
> > + * connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + /*
> > + * SHUTDOWN COM from client, this should not happen, let's close the
> > + * connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + /*
> > + * SHUTDOWN COMPLETE from server this is what we are expecting.
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > +
> > + /*
> > + * State : IP_VS_SCTP_S_SHUT_ACK_SER
> > + * SHUTDOWN ACK from the server, awaiting for SHUTDOWN COM from client
> > + */
> > + /*
> > + * We recieved the data chuck, keep the state unchanged. I assume
> > + * that still data chuncks can be received by both the peers in
> > + * SHUDOWN state
> > + */
> > +
> > + {{IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + /*
> > + * We have got an INIT from client. From the spec.âUpon receipt of
> > + * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
> > + * an INIT ACK using the same parameters it sent in its original
> > + * INIT chunk (including its Initiate Tag, unchangedâ).
> > + */
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + /*
> > + * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
> > + * âIf an INIT ACK is received by an endpoint in any state other
> > + * than the COOKIE-WAIT state, the endpoint should discard the
> > + * INIT ACK chunkâ. Stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + /*
> > + * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
> > + * peer and peer shall move to the ESTABISHED. if it doesn't handle
> > + * it will send ERROR chunk. So, stay in the same state
> > + */
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + /*
> > + * COOKIE ACK from client, not sure what to do stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + /*
> > + * SHUTDOWN sent from the client, move to SHUDDOWN_CLI
> > + */
> > + {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + /*
> > + * SHUTDOWN sent from the server, move to SHUTDOWN_SER
> > + */
> > + {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + /*
> > + * client sent SHUDTDOWN_ACK, this should not happen let's close
> > + * the connection.
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + /*
> > + * Server resent SHUTDOWN ACK, stay in the same state
> > + */
> > + {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + /*
> > + * SHUTDOWN COM from client, this what we are expecting, let's close
> > + * the connection
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + /*
> > + * SHUTDOWN COMPLETE from server this should not happen.
> > + */
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + },
> > + /*
> > + * State : IP_VS_SCTP_S_CLOSED
> > + */
> > + {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
> > + {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
> > + {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
> > + {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
> > + }
> > +};
> > +
> > +/*
> > + * Timeout table[state]
> > + */
> > +static int sctp_timeouts[IP_VS_SCTP_S_LAST + 1] = {
> > + [IP_VS_SCTP_S_NONE] = 2 * HZ,
> > + [IP_VS_SCTP_S_INIT_CLI] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_INIT_SER] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_INIT_ACK_CLI] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_INIT_ACK_SER] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_ECHO_CLI] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_ECHO_SER] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_ESTABLISHED] = 15 * 60 * HZ,
> > + [IP_VS_SCTP_S_SHUT_CLI] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_SHUT_SER] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_SHUT_ACK_CLI] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_SHUT_ACK_SER] = 1 * 60 * HZ,
> > + [IP_VS_SCTP_S_CLOSED] = 10 * HZ,
> > + [IP_VS_SCTP_S_LAST] = 2 * HZ,
> > +};
> > +
> > +static const char *sctp_state_name_table[IP_VS_SCTP_S_LAST + 1] = {
> > + [IP_VS_SCTP_S_NONE] = "NONE",
> > + [IP_VS_SCTP_S_INIT_CLI] = "INIT_CLI",
> > + [IP_VS_SCTP_S_INIT_SER] = "INIT_SER",
> > + [IP_VS_SCTP_S_INIT_ACK_CLI] = "INIT_ACK_CLI",
> > + [IP_VS_SCTP_S_INIT_ACK_SER] = "INIT_ACK_SER",
> > + [IP_VS_SCTP_S_ECHO_CLI] = "COOKIE_ECHO_CLI",
> > + [IP_VS_SCTP_S_ECHO_SER] = "COOKIE_ECHO_SER",
> > + [IP_VS_SCTP_S_ESTABLISHED] = "ESTABISHED",
> > + [IP_VS_SCTP_S_SHUT_CLI] = "SHUTDOWN_CLI",
> > + [IP_VS_SCTP_S_SHUT_SER] = "SHUTDOWN_CLI",
> > + [IP_VS_SCTP_S_SHUT_ACK_CLI] = "SHUTDOWN_ACK_CLI",
> > + [IP_VS_SCTP_S_SHUT_ACK_SER] = "SHUTDOWN_ACK_SER",
> > + [IP_VS_SCTP_S_CLOSED] = "CLOSED",
> > + [IP_VS_SCTP_S_LAST] = "BUG!"
> > +};
> > +
> > +
> > +static const char *sctp_state_name(int state)
> > +{
> > + if (state >= IP_VS_SCTP_S_LAST)
> > + return "ERR!";
> > + return sctp_state_name_table[state] ? sctp_state_name_table[state]
> > + : "?";
>
> Perhaps the following would be easier on the eye?
>
> if (sctp_state_name_table[state])
> return sctp_state_name_table[state];
> return "?";
>
> > +}
> > +
> > +static void sctp_timeout_change(struct ip_vs_protocol *pp, int flags)
> > +{
> > +
> > +
>
> No need for empty lines here.
> In particular, please don't use more than one consecutive blank line.
>
> > +}
> > +
> > +static int
> > +sctp_set_state_timeout(struct ip_vs_protocol *pp, char *sname, int to)
> > +{
> > +
> > +return ip_vs_set_state_timeout(pp->timeout_table, IP_VS_SCTP_S_LAST,
> > + sctp_state_name_table, sname, to);
>
> Need one more tab of indentation here.
>
> return ip_vs_set_state_timeout(pp->timeout_table, IP_VS_SCTP_S_LAST,
> sctp_state_name_table, sname, to);
>
> > +}
> > +
> > +static inline int
> > +set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp,
> > + int direction, const struct sk_buff *skb)
> > +{
> > + sctp_chunkhdr_t _sctpch, *sch;
> > + unsigned char chunk_type;
> > + int event, next_state;
> > + int ihl;
> > +
> > +#ifdef CONFIG_IP_VS_IPV6
> > + ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
> > +#else
> > + ihl = ip_hdrlen(skb);
> > +#endif
> > +
> > + sch = skb_header_pointer(skb, ihl + sizeof(sctp_sctphdr_t),
> > + sizeof(_sctpch), &_sctpch);
> > + if (sch == NULL)
> > + return 0;
> > +
> > + chunk_type = sch->type;
> > + /*
> > + * Section 3: Multiple chunks can be bundled into one SCTP packet
> > + * up to the MTU size, except for the INIT, INIT ACK, and
> > + * SHUTDOWN COMPLETE chunks. These chunks MUST NOT be bundled with
> > + * any other chunk in a packet.
> > + *
> > + * Section 3.3.7: DATA chunks MUST NOT be bundled with ABORT. Control
> > + * chunks (except for INIT, INIT ACK, and SHUTDOWN COMPLETE) MAY be
> > + * bundled with an ABORT, but they MUST be placed before the ABORT
> > + * in the SCTP packet or they will be ignored by the receiver.
> > + */
> > + if ((sch->type == SCTP_CID_COOKIE_ECHO) ||
> > + (sch->type == SCTP_CID_COOKIE_ACK)) {
> > + sch = skb_header_pointer(skb, (ihl + sizeof(sctp_sctphdr_t) +
> > + sch->length), sizeof(_sctpch), &_sctpch);
> > + if (sch) {
> > + if (sch->type == SCTP_CID_ABORT)
> > + chunk_type = sch->type;
> > + }
> > + }
> > +
> > + event = sctp_events[chunk_type];
> > +
> > + /*
> > + * If the direction is IP_VS_DIR_OUTPUT, this event is from server
> > + */
> > + if (direction == IP_VS_DIR_OUTPUT)
> > + event++;
> > + /*
> > + * get next state
> > + */
> > + next_state = sctp_states_table[cp->state][event].next_state;
> > +
> > + if (next_state != cp->state) {
> > + struct ip_vs_dest *dest = cp->dest;
> > +
> > + IP_VS_DBG_BUF(8, "%s %s %s:%d->"
> > + "%s:%d state: %s->%s conn->refcnt:%d\n",
> > + pp->name,
> > + ((direction == IP_VS_DIR_OUTPUT) ?
> > + "output " : "input "),
> > + IP_VS_DBG_ADDR(cp->af, &cp->daddr),
> > + ntohs(cp->dport),
> > + IP_VS_DBG_ADDR(cp->af, &cp->caddr),
> > + ntohs(cp->cport),
> > + sctp_state_name(cp->state),
> > + sctp_state_name(next_state),
> > + atomic_read(&cp->refcnt));
> > + if (dest) {
> > + if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
> > + (next_state != IP_VS_SCTP_S_ESTABLISHED)) {
> > + atomic_dec(&dest->activeconns);
> > + atomic_inc(&dest->inactconns);
> > + cp->flags |= IP_VS_CONN_F_INACTIVE;
> > + } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) &&
> > + (next_state == IP_VS_SCTP_S_ESTABLISHED)) {
> > + atomic_inc(&dest->activeconns);
> > + atomic_dec(&dest->inactconns);
> > + cp->flags &= ~IP_VS_CONN_F_INACTIVE;
> > + }
> > + }
> > + }
> > +
> > + cp->timeout = pp->timeout_table[cp->state = next_state];
> > +
> > + return 1;
> > +}
> > +
> > +static int
> > +sctp_state_transition(struct ip_vs_conn *cp, int direction,
> > + const struct sk_buff *skb, struct ip_vs_protocol *pp)
> > +{
> > + int ret = 0;
> > +
> > + spin_lock(&cp->lock);
> > + ret = set_sctp_state(pp, cp, direction, skb);
> > + spin_unlock(&cp->lock);
> > +
> > + return ret;
> > +}
> > +
> > +/*
> > + * Hash table for SCTP application incarnations
> > + */
> > +#define SCTP_APP_TAB_BITS 4
> > +#define SCTP_APP_TAB_SIZE (1 << SCTP_APP_TAB_BITS)
> > +#define SCTP_APP_TAB_MASK (SCTP_APP_TAB_SIZE - 1)
> > +
> > +static struct list_head sctp_apps[SCTP_APP_TAB_SIZE];
> > +static DEFINE_SPINLOCK(sctp_app_lock);
> > +
> > +static inline __u16 sctp_app_hashkey(__be16 port)
> > +{
> > + return (((__force u16)port >> SCTP_APP_TAB_BITS) ^ (__force u16)port)
> > + & SCTP_APP_TAB_MASK;
> > +}
> > +
> > +static int sctp_register_app(struct ip_vs_app *inc)
> > +{
> > + struct ip_vs_app *i;
> > + __u16 hash;
> > + __be16 port = inc->port;
> > + int ret = 0;
> > +
> > + hash = sctp_app_hashkey(port);
> > +
> > + spin_lock_bh(&sctp_app_lock);
> > + list_for_each_entry(i, &sctp_apps[hash], p_list) {
> > + if (i->port == port) {
> > + ret = -EEXIST;
> > + goto out;
> > + }
> > + }
> > + list_add(&inc->p_list, &sctp_apps[hash]);
> > + atomic_inc(&ip_vs_protocol_sctp.appcnt);
> > +out:
> > + spin_unlock_bh(&sctp_app_lock);
> > +
> > + return ret;
> > +}
> > +
> > +static void sctp_unregister_app(struct ip_vs_app *inc)
> > +{
> > + spin_lock_bh(&sctp_app_lock);
> > + atomic_dec(&ip_vs_protocol_sctp.appcnt);
> > + list_del(&inc->p_list);
> > + spin_unlock_bh(&sctp_app_lock);
> > +}
> > +
> > +static int sctp_app_conn_bind(struct ip_vs_conn *cp)
> > +{
> > + int hash;
> > + struct ip_vs_app *inc;
> > + int result = 0;
> > +
> > + /* Default binding: bind app only for NAT */
> > + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
> > + return 0;
> > + /* Lookup application incarnations and bind the right one */
> > + hash = sctp_app_hashkey(cp->vport);
> > +
> > + spin_lock(&sctp_app_lock);
> > + list_for_each_entry(inc, &sctp_apps[hash], p_list) {
> > + if (inc->port == cp->vport) {
> > + if (unlikely(!ip_vs_app_inc_get(inc)))
> > + break;
> > + spin_unlock(&sctp_app_lock);
>
> Is it necessary to spin_unlock() here?
> If not, out: could also spin_unlock() which would
> make the locking more obvious. But as you have
> done that in sctp_register_app() I guess you
> have a reason for spin_unlock()ing here.
>
> > +
> > + IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->"
> > + "%s:%u to app %s on port %u\n",
> > + __func__,
> > + IP_VS_DBG_ADDR(cp->af, &cp->caddr),
> > + ntohs(cp->cport),
> > + IP_VS_DBG_ADDR(cp->af, &cp->vaddr),
> > + ntohs(cp->vport),
> > + inc->name, ntohs(inc->port));
> > + cp->app = inc;
> > + if (inc->init_conn)
> > + result = inc->init_conn(inc, cp);
> > + goto out;
> > + }
> > + }
> > + spin_unlock(&sctp_app_lock);
> > +out:
> > + return result;
> > +}
> > +
> > +static void ip_vs_sctp_init(struct ip_vs_protocol *pp)
> > +{
> > + IP_VS_INIT_HASH_TABLE(sctp_apps);
> > + pp->timeout_table = sctp_timeouts;
> > +}
> > +
> > +
> > +static void ip_vs_sctp_exit(struct ip_vs_protocol *pp)
> > +{
> > +
> > +}
> > +
> > +struct ip_vs_protocol ip_vs_protocol_sctp = {
> > + .name = "SCTP",
> > + .protocol = IPPROTO_SCTP,
> > + .num_states = IP_VS_SCTP_S_LAST,
> > + .dont_defrag = 0,
> > + .appcnt = ATOMIC_INIT(0),
> > + .init = ip_vs_sctp_init,
> > + .exit = ip_vs_sctp_exit,
> > + .register_app = sctp_register_app,
> > + .unregister_app = sctp_unregister_app,
> > + .conn_schedule = sctp_conn_schedule,
> > + .conn_in_get = sctp_conn_in_get,
> > + .conn_out_get = sctp_conn_out_get,
> > + .snat_handler = sctp_snat_handler,
> > + .dnat_handler = sctp_dnat_handler,
> > + .csum_check = sctp_csum_check,
> > + .state_name = sctp_state_name,
> > + .state_transition = sctp_state_transition,
> > + .app_conn_bind = sctp_app_conn_bind,
> > + .debug_packet = ip_vs_tcpudp_debug_packet,
> > + .timeout_change = sctp_timeout_change,
> > + .set_state_timeout = sctp_set_state_timeout,
> > +};
> > diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
> > index e177f0d..2b93016 100644
> > --- a/net/netfilter/ipvs/ip_vs_sync.c
> > +++ b/net/netfilter/ipvs/ip_vs_sync.c
> > @@ -400,6 +400,11 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
> > flags |= IP_VS_CONN_F_INACTIVE;
> > else
> > flags &= ~IP_VS_CONN_F_INACTIVE;
> > + } else if (s->protocol == IPPROTO_SCTP) {
> > + if (state != IP_VS_SCTP_S_ESTABLISHED)
> > + flags |= IP_VS_CONN_F_INACTIVE;
> > + else
> > + flags &= ~IP_VS_CONN_F_INACTIVE;
> > }
> > cp = ip_vs_conn_new(AF_INET, s->protocol,
> > (union nf_inet_addr *)&s->caddr,
> > @@ -434,6 +439,15 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
> > atomic_dec(&dest->inactconns);
> > cp->flags &= ~IP_VS_CONN_F_INACTIVE;
> > }
> > + } else if ((cp->dest) && (cp->protocol == IPPROTO_SCTP) &&
> > + (cp->state != state)) {
> > + dest = cp->dest;
> > + if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
> > + (state != IP_VS_SCTP_S_ESTABLISHED)) {
> > + atomic_dec(&dest->activeconns);
> > + atomic_inc(&dest->inactconns);
> > + cp->flags &= ~IP_VS_CONN_F_INACTIVE;
> > + }
> > }
> >
> > if (opt)
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe lvs-devel" in
> > the body of a message to majordomo@xxxxxxxxxxxxxxx
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/