diff --git a/src/native/sctp/Makefile.nmake b/src/native/sctp/Makefile.nmake
new file mode 100644
index 0000000000000000000000000000000000000000..484a6c8e3d7e496bab0c7e1dd61fced889968b5c
--- /dev/null
+++ b/src/native/sctp/Makefile.nmake
@@ -0,0 +1,25 @@
+CC = cl /O2
+JNI_HEADERS = /I"%JAVA_HOME%\include" /I"%JAVA_HOME%\include\win32"
+
+SCTP_HEADERS = /I"sctp-refimpl-read-only\KERN\usrsctp\usrsctplib"
+LIBS = /link /LIBPATH:"sctp-refimpl-read-only\KERN\usrsctp\usrsctplib" /LIBPATH:"%JAVA_HOME%\lib"
+
+#CFLAGS = $(JNI_HEADERS) $(SCTP_HEADERS) -DSCTP_DEBUG=1 -DINET6=1
+CFLAGS = $(JNI_HEADERS) $(SCTP_HEADERS) -DINET6=1
+
+OBJS = org_jitsi_sctp4j_Sctp.c
+
+jnsctp.dll: clean
+	$(CC) $(CFLAGS) /LD $(OBJS) $(LIBS) /out:jnsctp.dll usrsctp.lib
+
+install32: jnsctp.dll
+	copy jnsctp.dll "../../../lib/native/windows/jnsctp.dll"
+	copy jnsctp.dll "../../../../jitsi-videobridge/lib/native/windows/jnsctp.dll"
+
+install64: jnsctp.dll
+	copy jnsctp.dll "../../../lib/native/windows-64/jnsctp.dll"
+	copy jnsctp.dll "../../../../jitsi-videobridge/lib/native/windows-64/jnsctp.dll"
+
+clean:
+	del *.exp *.lib *.dll *.obj *.manifest 
+
diff --git a/src/native/sctp/README b/src/native/sctp/README
new file mode 100644
index 0000000000000000000000000000000000000000..0b3861134548780e12fb1c1a166e676eea621eeb
--- /dev/null
+++ b/src/native/sctp/README
@@ -0,0 +1,73 @@
+# BUILD INSTRUCTIONS FOR WINDOWS
+#
+# 1. Checkout usrsctp source:
+#
+#   a) cd src/native/sctp
+#
+#   b) svn checkout http://sctp-refimpl.googlecode.com/svn/trunk/ sctp-refimpl-read-only
+#
+# 2. Build usrsctp:
+#
+#   a) open Visual Studio console(x86 for 32 bit, 64 for 64 bit)
+#
+#   b) cd src/native/sctp/sctp-refimpl-read-only/KERN/usrsctp/
+#
+#   c) nmake -f Makefile.nmake
+#
+# 3. Build native Sctp wrapper:
+#
+# a) cd src/native/sctp
+#
+# b) build and install
+#
+#    32bit:
+#      nmake -f Makfile.nmake install32
+#
+#    64bit:
+#      nmake -f Makfile.nmake install64
+#
+# Reminder:
+# The command below is used to re-generate jni header
+# (run from classes output folder)
+# javah -jni org.jitsi.sctp4j.Sctp
+#
+# BUILD INSTRUCTIONS FOR LINUX
+#
+#
+# BUILD USRSCTP LIB (required to build/run native SCTP wrapper)
+#
+# 1. Go to dir src/native/sctp
+#
+#    cd src/native/sctp
+#
+# 2. Checkout usrsctp
+#
+#    svn checkout http://sctp-refimpl.googlecode.com/svn/trunk/ sctp-refimpl-read-only
+#
+# 3. Go to usrsctp src root
+#
+#    cd sctp-refimpl-read-only/KERN/usrsctp
+#
+# 4. Build and install usrsctp lib
+#
+#    a) libtoolize
+#
+#    b) aclocal
+#
+#    c) autoconf
+#
+#    d) touch AUTHORS NEWS README ChangeLog
+#
+#    e) automake --add-missing
+#
+#    f) ./configure --prefix=/usr
+#
+#    g) sudo make install
+#
+# BUILD NATIVE WRAPPER FOR SCTP
+#
+# 1. Go to dir src/native/sctp
+# 2. 32 bit:
+#     make install32
+#    64 bit:
+#     make install64
\ No newline at end of file
diff --git a/src/native/sctp/makefile b/src/native/sctp/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..d3cee1b1dcec31f44f6a6708d98b0d09a5d76d66
--- /dev/null
+++ b/src/native/sctp/makefile
@@ -0,0 +1,25 @@
+CC=gcc
+
+JNI_HEADERS = -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/linux"
+SCTP_HEADERS = -I"sctp-refimpl-read-only/KERN/usrsctp/usrsctplib"
+
+#CFLAGS = -Wall -Werror -std=c99 $(JNI_HEADERS) $(SCTP_HEADERS) -DSCTP_DEBUG=1 -DINET6=1
+CFLAGS = -Wall -Werror -std=c99 $(JNI_HEADERS) $(SCTP_HEADERS) -DINET6=1
+
+OBJS = org_jitsi_sctp4j_Sctp.c
+
+LIBS = -lusrsctp -lpthread
+
+libjnsctp.so: clean
+	$(CC) $(CFLAGS) -fPIC -shared $(OBJS) -o libjnsctp.so $(LIBS)
+
+install32: libjnsctp.so
+	cp libjnsctp.so "../../../lib/native/linux/libjnsctp.so"
+	cp libjnsctp.so "../../../../jitsi-videobridge/lib/native/linux/libjnsctp.so"
+
+install64: libjnsctp.so
+	cp libjnsctp.so "../../../lib/native/linux-64/libjnsctp.so"
+	cp libjnsctp.so "../../../../jitsi-videobridge/lib/native/linux-64/libjnsctp.so"
+
+clean:
+	rm -f *.so
diff --git a/src/native/sctp/org_jitsi_sctp4j_Sctp.c b/src/native/sctp/org_jitsi_sctp4j_Sctp.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ad83f82f15478ef7f0c8b1a6cc2f338a01fe124
--- /dev/null
+++ b/src/native/sctp/org_jitsi_sctp4j_Sctp.c
@@ -0,0 +1,659 @@
+#include "org_jitsi_sctp4j_Sctp.h"
+#include <usrsctp.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+// errno returned after connect call on success
+#define SCTP_EINPROGRESS 115
+
+// Name of the class that contains callback methods.
+#define SCTP_CLASS "org/jitsi/sctp4j/Sctp"
+
+// Struct used to identify Sctp sockets
+struct sctp_socket
+{
+    // Socket object created by SCTP stack
+    struct socket *sock;
+    
+    int localPort;
+};
+
+// Java Virtual Machine instance
+JavaVM* jvm;
+
+void callOnSctpOutboundPacket( void*   socketPtr, void*   data,
+                               size_t  length,    uint8_t tos,
+                               uint8_t set_df )
+{
+    JNIEnv* jniEnv;
+    
+    int hadToAttach = 0;
+    
+    int getEnvStat;
+	
+	jclass sctpClass;    
+    jmethodID outboundCallback;
+    
+    jlong sctpPtr;  
+    jbyteArray jBuff;    
+    jint jtos;    
+    jint jset_df;
+	
+	getEnvStat = (*jvm)->GetEnv(jvm, (void **)&jniEnv, JNI_VERSION_1_6);
+    if (getEnvStat == JNI_EDETACHED) 
+    {
+        hadToAttach = 1;
+        
+        if ((*jvm)->AttachCurrentThread(jvm, (void **) &jniEnv, NULL) != 0)
+        {
+          printf("Failed to attach new thread\n");
+          return;
+        }
+    }
+    else if (getEnvStat == JNI_EVERSION) 
+    {
+        printf("GetEnv: version not supported\n");
+        return;
+    }
+    else if (getEnvStat == JNI_OK) 
+    {
+        // OK
+    }
+    
+    sctpClass = (*jniEnv)->FindClass(jniEnv, SCTP_CLASS);
+    if(!sctpClass)
+    {
+        printf("Failed to get SCTP class\n");
+        return;
+    }
+
+
+    outboundCallback = (*jniEnv)->GetStaticMethodID(
+        jniEnv, sctpClass, "onSctpOutboundPacket", "(J[BII)V");
+
+    if(!outboundCallback)
+    {
+        printf("Failed to get onSctpOutboundPacket method\n");
+        return;
+    }
+    
+    sctpPtr = (jlong)(long)socketPtr;
+  
+    jBuff = (*jniEnv)->NewByteArray(jniEnv, length);       
+    (*jniEnv)->SetByteArrayRegion(jniEnv, jBuff, 0, length, (jbyte*) data);
+    
+    jtos = (jint)tos;
+    
+    jset_df = (jint)set_df;
+    
+    (*jniEnv)->CallStaticObjectMethod(
+        jniEnv, sctpClass, outboundCallback, sctpPtr, jBuff, jtos, jset_df);
+
+    // FIXME: not sure if jBuff should be released
+    // Release byte array
+    //(*jniEnv)->ReleaseByteArrayElements(jniEnv, jBuff, packetDataPtr,
+      //  JNI_ABORT/*free the buffer without copying back the possible changes */);
+    (*jniEnv)->DeleteLocalRef(jniEnv, jBuff);
+      
+    if(hadToAttach)
+    {  
+        if ((*jvm)->DetachCurrentThread(jvm) != 0) 
+        {
+            printf("Failed to deattach the thread\n");
+        }  
+    }
+}
+
+void callOnSctpInboundPacket( void*    socketPtr, void*    data,
+                              size_t   length,    uint16_t sid,
+                              uint16_t ssn,       uint16_t tsn,
+                              uint32_t ppid,      uint16_t context,
+                              int      flags )
+{
+    JNIEnv* jniEnv;
+    
+    int hadToAttach = 0;
+	
+	int getEnvStat;
+	
+	jclass sctpClass;    
+    jmethodID inboundCallback;
+    
+    jlong sctpPtr;
+	
+    jbyteArray jBuff;
+    
+    jint jsid;
+    jint jssn;
+    jint jtsn;
+    jlong jppid;
+    jint jcontext;
+    
+    getEnvStat = (*jvm)->GetEnv(jvm, (void **)&jniEnv, JNI_VERSION_1_6);
+    if (getEnvStat == JNI_EDETACHED) 
+    {
+        hadToAttach = 1;
+        
+        if ((*jvm)->AttachCurrentThread(jvm, (void **) &jniEnv, NULL) != 0)
+        {
+          printf("Failed to attach new thread\n");
+          return;
+        }
+    }
+    else if (getEnvStat == JNI_EVERSION) 
+    {
+        printf("GetEnv: version not supported\n");
+        return;
+    }
+    else if (getEnvStat == JNI_OK) 
+    {
+        // OK
+    }
+    
+    sctpClass = (*jniEnv)->FindClass(jniEnv, SCTP_CLASS);
+    if(!sctpClass)
+    {
+        printf("Failed to get SCTP class\n");
+        return;
+    }
+    
+    inboundCallback = (*jniEnv)->GetStaticMethodID(
+        jniEnv, sctpClass, "onSctpInboundPacket", "(J[BIIIJII)V");
+
+    if(!inboundCallback)
+    {
+        printf("Failed to get onSctpInboundPacket method\n");
+        return;
+    }
+    
+    sctpPtr = (jlong)(long)socketPtr;
+  
+    jBuff = (*jniEnv)->NewByteArray(jniEnv, length);       
+    (*jniEnv)->SetByteArrayRegion(jniEnv, jBuff, 0, length, (jbyte*) data);
+    
+    jsid = (jint)sid;
+    jssn = (jint)ssn;
+    jtsn = (jint)tsn;
+    jppid = (jlong)ntohl(ppid);
+    jcontext = (jint)context;
+    
+    (*jniEnv)->CallStaticObjectMethod(
+        jniEnv, sctpClass, inboundCallback, sctpPtr, jBuff, jsid, jssn, jtsn,
+        jppid, jcontext, (jint)flags);
+    
+    // Release byte array
+    //(*jniEnv)->ReleaseByteArrayElements(jniEnv, jBuff, packetDataPtr, 
+      //  JNI_ABORT/*free the buffer without copying back the possible changes */);
+    (*jniEnv)->DeleteLocalRef(jniEnv, jBuff);
+      
+    if(hadToAttach)
+    {  
+        if ((*jvm)->DetachCurrentThread(jvm) != 0) 
+        {
+            printf("Failed to deattach the thread\n");
+        }  
+    }        
+}
+
+static int onSctpOutboundPacket(void*   addr, void*     data, size_t  length,
+                                uint8_t tos,  uint8_t set_df )
+{
+  if(data && length)
+  {
+    callOnSctpOutboundPacket(addr, data, length, tos, set_df);
+  }
+
+  return 0;
+}
+
+void debugSctpPrintf(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vprintf(format, ap);
+	va_end(ap);
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_init
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1init
+  (JNIEnv *env, jclass class, jint port)
+{
+    int status = (*env)->GetJavaVM(env, &jvm);
+    if(status != 0)
+    {
+        return JNI_FALSE;
+    }
+
+    // First argument is udp_encapsulation_port, which is not releveant for our
+    // AF_CONN use of sctp.
+    usrsctp_init((int)port, onSctpOutboundPacket, debugSctpPrintf);
+
+#ifdef SCTP_DEBUG
+    usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
+#endif
+
+    // TODO(ldixon): Consider turning this on/off.
+    usrsctp_sysctl_set_sctp_ecn_enable(0);
+    
+    //usrsctp_sysctl_set_sctp_blackhole(2);
+    
+    //usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(32);
+
+    return JNI_TRUE;
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    on_network_in
+ * Signature: (J[B)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_on_1network_1in
+  (JNIEnv *env, jclass class, jlong ptr, jbyteArray jbytesPacket)
+{
+    struct sctp_socket* sock;
+	jbyte* packetDataPtr;
+    jsize packetLength;
+	
+    sock = (struct sctp_socket*)(long)ptr;
+    
+    packetDataPtr = (*env)->GetByteArrayElements(
+        env, jbytesPacket, JNI_FALSE/* not a copy */);
+
+    packetLength = (*env)->GetArrayLength(env, jbytesPacket);
+    
+    usrsctp_conninput(sock, (char*)packetDataPtr, packetLength, 0);
+
+    (*env)->ReleaseByteArrayElements(env, jbytesPacket, packetDataPtr, 
+        JNI_ABORT/*free the buffer without copying back the possible changes */);
+}
+
+// This is the callback called from usrsctp when data has been received, after
+// a packet has been interpreted and parsed by usrsctp and found to contain
+// payload data. It is called by a usrsctp thread. It is assumed this function
+// will free the memory used by 'data'.
+int onSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
+                        void*  data, size_t length, struct sctp_rcvinfo rcv,
+                        int flags, void* ulp_info)
+{
+	//union sctp_notification* notification;
+	
+    if(data)
+    {
+    	if (flags & MSG_NOTIFICATION) 
+        {
+            //printf("SCTP NOTIFICATION F: %i L: %i\n", flags, (int)length);
+            //notification = (union sctp_notification*)data;
+            //printf("NOTIFI T: %u F: %u L: %u\n",
+            //    notification->sn_header.sn_type,
+            //    notification->sn_header.sn_flags,
+            //    notification->sn_header.sn_length
+            //);
+        }
+        else
+        {
+            // Pass packet data to Java
+		    callOnSctpInboundPacket(
+		        ulp_info,
+		        data,         length,
+		        rcv.rcv_sid,  rcv.rcv_ssn,     rcv.rcv_tsn,
+		        rcv.rcv_ppid, rcv.rcv_context, flags );
+		    
+        }
+        free(data);
+    }
+    return (1);
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_send
+ * Signature: (J[BZII)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1send
+  (JNIEnv   *env,    jclass     class,
+   jlong    ptr,     jbyteArray jdata,
+   jboolean ordered, jint       sid,  jint  ppid )
+{
+    struct sctp_socket* sctpSocket;
+    struct sctp_sndinfo sndinfo;
+
+    // data using SCTP.
+    ssize_t send_res = 0;  // result from usrsctp_sendv.
+    //struct sctp_sendv_spa spa;
+	
+	jbyte* dataPtr;
+    jsize dataLength;
+	
+	sctpSocket = (struct sctp_socket*)((long)ptr);    
+    
+    //memset(&spa, 0, sizeof(struct sctp_sendv_spa));
+    //spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
+    //spa.sendv_sndinfo.snd_sid = sid;//params.ssrc;
+    //spa.sendv_sndinfo.snd_ppid = htonl(ppid);//talk_base::HostToNetwork32(GetPpid(params.type));
+
+    // Ordered implies reliable.
+    //if (ordered != JNI_TRUE)
+    //{
+    //    spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
+        /*if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0)
+        {
+            spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
+            spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
+            spa.sendv_prinfo.pr_value = params.max_rtx_count;
+        }
+        else
+        {
+            spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
+            spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
+            spa.sendv_prinfo.pr_value = params.max_rtx_ms;
+        }*/
+    //}
+
+    dataPtr = (*env)->GetByteArrayElements(env, jdata, JNI_FALSE/* not a copy */);
+    dataLength = (*env)->GetArrayLength(env, jdata);
+
+    // We don't fragment.
+    //send_res = usrsctp_sendv(sctpSocket->sock, dataPtr, dataLength,
+      //                       NULL, 0, &spa, sizeof(spa),
+        //                     SCTP_SENDV_SPA, 0);
+        
+
+    sndinfo.snd_sid = sid;
+	sndinfo.snd_flags = 0;//8;
+	sndinfo.snd_ppid = htonl(ppid);
+	sndinfo.snd_context = 0;
+	sndinfo.snd_assoc_id = 0;
+	if(ordered != JNI_TRUE)
+	{
+	    sndinfo.snd_flags |= SCTP_UNORDERED;
+	}
+
+    send_res = usrsctp_sendv(
+        sctpSocket->sock, dataPtr, dataLength, NULL, 0, (void *)&sndinfo,
+        (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0);
+                            
+    /*send_res = usrsctp_sendv(
+        sctpSocket->sock, dataPtr, dataLength, 
+        NULL, 0, NULL, 0, SCTP_SENDV_NOINFO, 0);*/
+                             
+    (*env)->ReleaseByteArrayElements(env, jdata, dataPtr, 
+        JNI_ABORT/*free the buffer without copying back the possible changes */);                             
+                             
+    if (send_res < 0) 
+    {
+        perror("Sctp send error: ");
+    }
+    
+    return (jint)send_res;
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usersctp_socket
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL Java_org_jitsi_sctp4j_Sctp_usersctp_1socket
+  (JNIEnv *env, jclass class, jint localPort)
+{
+    struct sctp_socket* sctpSocket = malloc(sizeof(struct sctp_socket));
+	struct socket *sock;
+	struct linger linger_opt;
+    struct sctp_assoc_value stream_rst;
+    uint32_t nodelay = 1;
+	size_t i;
+	
+    int event_types[] = {SCTP_ASSOC_CHANGE,
+                         SCTP_PEER_ADDR_CHANGE,
+                         SCTP_SEND_FAILED_EVENT,
+                         SCTP_SENDER_DRY_EVENT,
+                         SCTP_STREAM_RESET_EVENT};
+    struct sctp_event event;
+    
+    // Register this class as an address for usrsctp. This is used by SCTP to
+    // direct the packets received (by the created socket) to this class.
+    usrsctp_register_address((void*)sctpSocket);
+
+    sock = usrsctp_socket( AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
+                           onSctpInboundPacket,  NULL, 0, (void*)sctpSocket);
+    if (sock == NULL)
+    {
+        perror("userspace_socket");
+        free(sctpSocket);
+        return 0;
+    }
+    
+    // Make the socket non-blocking. Connect, close, shutdown etc will not block
+    // the thread waiting for the socket operation to complete.
+    if (usrsctp_set_non_blocking(sock, 1) < 0)
+    {
+      perror("Failed to set SCTP to non blocking.");
+      free(sctpSocket);
+      return 0;
+    }
+
+    // This ensures that the usrsctp close call deletes the association. This
+    // prevents usrsctp from calling OnSctpOutboundPacket with references to
+    // this class as the address.    
+    linger_opt.l_onoff = 1;
+    linger_opt.l_linger = 0;
+    if (usrsctp_setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger_opt,
+                           sizeof(linger_opt)))
+    {
+      perror("Failed to set SO_LINGER.");
+      free(sctpSocket);
+      return 0;
+    }
+
+    // Enable stream ID resets.
+    stream_rst.assoc_id = SCTP_ALL_ASSOC;
+    stream_rst.assoc_value = 1;
+    if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
+                           &stream_rst, sizeof(stream_rst)))
+    {
+      perror("Failed to set SCTP_ENABLE_STREAM_RESET.");
+      free(sctpSocket);
+      return 0;
+    }
+
+    // Nagle.
+    if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
+                           sizeof(nodelay)))
+    {
+      perror("Failed to set SCTP_NODELAY.");
+      free(sctpSocket);
+      return 0;
+    }
+
+    // Subscribe to SCTP event notifications.
+    memset(&event, 0, sizeof(struct sctp_event));
+    event.se_assoc_id = SCTP_ALL_ASSOC;
+    event.se_on = 1;
+    for (i = 0; i < 5; i++)
+    {
+        event.se_type = event_types[i];
+        if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENT, &event,
+                               sizeof(event)) < 0)
+        {
+            printf("Failed to set SCTP_EVENT type: %i\n", event.se_type);
+            free(sctpSocket);
+            return 0;
+        }
+    }
+
+    sctpSocket->sock = sock;
+    sctpSocket->localPort = (int)localPort;
+    
+    return (jlong)((long)sctpSocket);
+}
+
+struct sockaddr_conn getSctpSockAddr(int port, void* adr)
+{
+    struct sockaddr_conn sconn;
+    memset(&sconn, 0, sizeof(struct sockaddr_conn));
+	sconn.sconn_family = AF_CONN;
+#ifdef HAVE_SCONN_LEN
+	sconn.sconn_len = sizeof(struct sockaddr_conn);
+#endif
+	sconn.sconn_port = htons(port);
+	sconn.sconn_addr = adr;
+    return sconn;
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_listen
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1listen
+  (JNIEnv *env, jclass class, jlong ptr)
+{
+    struct sctp_socket* sctpSocket;
+	struct sockaddr_conn sconn;
+	
+    sctpSocket = (struct sctp_socket*)((long)ptr);    
+    
+    sconn = getSctpSockAddr(sctpSocket->localPort, (void*)sctpSocket);
+    
+    /* Bind server socket */
+	if (usrsctp_bind( sctpSocket->sock,
+	                  (struct sockaddr *) &sconn,
+	                  sizeof(struct sockaddr_conn)) < 0)
+    {
+		perror("usrsctp_bind");
+	}
+    
+    /* Make server side passive... */
+	if (usrsctp_listen(sctpSocket->sock, 1) < 0)
+	{
+		perror("usrsctp_listen");
+	}
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_accept
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1accept
+  (JNIEnv *env, jclass class, jlong ptr)
+{
+    struct sctp_socket* sctpSocket;
+    struct socket* acceptedSocket;
+	
+	sctpSocket = (struct sctp_socket*)((long)ptr);
+
+	while((acceptedSocket = usrsctp_accept(sctpSocket->sock, NULL, NULL))
+	        == NULL)
+	{
+    	//perror("usrsctp_accept");
+	}
+	usrsctp_close(sctpSocket->sock);
+	sctpSocket->sock = acceptedSocket;
+}
+
+int connectSctp(struct sctp_socket *sctp_socket, int remotePort)
+{
+    struct socket* sock;
+	struct sockaddr_conn sconn;
+	int connect_result;
+
+	sock = sctp_socket->sock;
+
+    sconn = getSctpSockAddr(sctp_socket->localPort, (void*)sctp_socket);
+
+	if (usrsctp_bind( sock,
+	                  (struct sockaddr *)&sconn,
+	                  sizeof(struct sockaddr_conn)) < 0)
+    {
+		perror("usrsctp_bind");
+		return 0;
+	}
+
+    sconn = getSctpSockAddr(remotePort, (void*)sctp_socket);
+    connect_result = usrsctp_connect(
+        sock, (struct sockaddr *)&sconn, sizeof(struct sockaddr_conn));
+
+//#ifdef _WIN32
+  //  if (connect_result == SOCKET_ERROR)
+    //{
+      //  perror("usrsctp_connect");
+        //return 0;
+//    }
+//#else
+// FIXME: on windows "Unknown error" is returned but the socket works anyway...
+    if (connect_result < 0 && errno != SCTP_EINPROGRESS)
+    {
+        perror("usrsctp_connect");
+        return 0;
+    }
+//#endif
+    return 1;
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_connect
+ * Signature: (JI)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1connect
+  (JNIEnv *env, jclass class, jlong ptr, jint remotePort)
+{
+    struct sctp_socket* sctpSocket;
+
+    sctpSocket = (struct sctp_socket*)((void*)ptr);
+    // Try connecting the socket
+    if(connectSctp(sctpSocket, (int)remotePort))
+    {
+        return JNI_TRUE;
+    }
+    else
+    {
+        return JNI_FALSE;
+    }
+}
+
+void closeSocket(struct sctp_socket* sctp)
+{
+    usrsctp_close(sctp->sock);
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_close
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1close
+  (JNIEnv *env, jclass class, jlong ptr)
+{   
+    struct sctp_socket* sctp;
+    sctp = (struct sctp_socket*)((long)ptr);
+
+    closeSocket(sctp);
+
+    free(sctp);    
+}
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_finish
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1finish
+  (JNIEnv *env, jclass class)
+{
+    if(usrsctp_finish() != 0)
+    {
+	    return JNI_TRUE;
+	}
+	else 
+	{
+        return JNI_FALSE;
+    }
+}
+
diff --git a/src/native/sctp/org_jitsi_sctp4j_Sctp.h b/src/native/sctp/org_jitsi_sctp4j_Sctp.h
new file mode 100644
index 0000000000000000000000000000000000000000..3bec962c4fa64f292dc5f37f5dd9b01f8b589335
--- /dev/null
+++ b/src/native/sctp/org_jitsi_sctp4j_Sctp.h
@@ -0,0 +1,163 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_jitsi_sctp4j_Sctp */
+
+#ifndef _Included_org_jitsi_sctp4j_Sctp
+#define _Included_org_jitsi_sctp4j_Sctp
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_jitsi_sctp4j_Sctp_MSG_NOTIFICATION
+#define org_jitsi_sctp4j_Sctp_MSG_NOTIFICATION 8192L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_CHANGE
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_CHANGE 1L
+#undef org_jitsi_sctp4j_Sctp_SCTP_PEER_ADDR_CHANGE
+#define org_jitsi_sctp4j_Sctp_SCTP_PEER_ADDR_CHANGE 2L
+#undef org_jitsi_sctp4j_Sctp_SCTP_REMOTE_ERROR
+#define org_jitsi_sctp4j_Sctp_SCTP_REMOTE_ERROR 3L
+#undef org_jitsi_sctp4j_Sctp_SCTP_SEND_FAILED
+#define org_jitsi_sctp4j_Sctp_SCTP_SEND_FAILED 4L
+#undef org_jitsi_sctp4j_Sctp_SCTP_SHUTDOWN_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_SHUTDOWN_EVENT 5L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ADAPTATION_INDICATION
+#define org_jitsi_sctp4j_Sctp_SCTP_ADAPTATION_INDICATION 6L
+#undef org_jitsi_sctp4j_Sctp_SCTP_PARTIAL_DELIVERY_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_PARTIAL_DELIVERY_EVENT 7L
+#undef org_jitsi_sctp4j_Sctp_SCTP_AUTHENTICATION_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_AUTHENTICATION_EVENT 8L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_EVENT 9L
+#undef org_jitsi_sctp4j_Sctp_SCTP_SENDER_DRY_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_SENDER_DRY_EVENT 10L
+#undef org_jitsi_sctp4j_Sctp_SCTP_NOTIFICATIONS_STOPPED_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_NOTIFICATIONS_STOPPED_EVENT 11L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_RESET_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_RESET_EVENT 12L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_CHANGE_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_CHANGE_EVENT 13L
+#undef org_jitsi_sctp4j_Sctp_SCTP_SEND_FAILED_EVENT
+#define org_jitsi_sctp4j_Sctp_SCTP_SEND_FAILED_EVENT 14L
+#undef org_jitsi_sctp4j_Sctp_SCTP_COMM_UP
+#define org_jitsi_sctp4j_Sctp_SCTP_COMM_UP 1L
+#undef org_jitsi_sctp4j_Sctp_SCTP_COMM_LOST
+#define org_jitsi_sctp4j_Sctp_SCTP_COMM_LOST 2L
+#undef org_jitsi_sctp4j_Sctp_SCTP_RESTART
+#define org_jitsi_sctp4j_Sctp_SCTP_RESTART 3L
+#undef org_jitsi_sctp4j_Sctp_SCTP_SHUTDOWN_COMP
+#define org_jitsi_sctp4j_Sctp_SCTP_SHUTDOWN_COMP 4L
+#undef org_jitsi_sctp4j_Sctp_SCTP_CANT_STR_ASSOC
+#define org_jitsi_sctp4j_Sctp_SCTP_CANT_STR_ASSOC 5L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_PR
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_PR 1L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_AUTH
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_AUTH 2L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_ASCONF
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_ASCONF 3L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_MULTIBUF
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_MULTIBUF 4L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_RE_CONFIG
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_RE_CONFIG 5L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_MAX
+#define org_jitsi_sctp4j_Sctp_SCTP_ASSOC_SUPPORTS_MAX 5L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ADDR_AVAILABLE
+#define org_jitsi_sctp4j_Sctp_SCTP_ADDR_AVAILABLE 1L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ADDR_UNREACHABLE
+#define org_jitsi_sctp4j_Sctp_SCTP_ADDR_UNREACHABLE 2L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ADDR_REMOVED
+#define org_jitsi_sctp4j_Sctp_SCTP_ADDR_REMOVED 3L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ADDR_ADDED
+#define org_jitsi_sctp4j_Sctp_SCTP_ADDR_ADDED 4L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ADDR_MADE_PRIM
+#define org_jitsi_sctp4j_Sctp_SCTP_ADDR_MADE_PRIM 5L
+#undef org_jitsi_sctp4j_Sctp_SCTP_ADDR_CONFIRMED
+#define org_jitsi_sctp4j_Sctp_SCTP_ADDR_CONFIRMED 6L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_INCOMING_SSN
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_INCOMING_SSN 1L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_OUTGOING_SSN
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_OUTGOING_SSN 2L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_DENIED
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_DENIED 4L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_FAILED
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_FAILED 8L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_CHANGED_DENIED
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_CHANGED_DENIED 16L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_INCOMING
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_INCOMING 1L
+#undef org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_OUTGOING
+#define org_jitsi_sctp4j_Sctp_SCTP_STREAM_RESET_OUTGOING 2L
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_init
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1init
+  (JNIEnv *, jclass, jint);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usersctp_socket
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL Java_org_jitsi_sctp4j_Sctp_usersctp_1socket
+  (JNIEnv *, jclass, jint);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_send
+ * Signature: (J[BZII)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1send
+  (JNIEnv *, jclass, jlong, jbyteArray, jboolean, jint, jint);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_listen
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1listen
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_accept
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1accept
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_connect
+ * Signature: (JI)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1connect
+  (JNIEnv *, jclass, jlong, jint);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_close
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1close
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    usrsctp_finish
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_sctp4j_Sctp_usrsctp_1finish
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_jitsi_sctp4j_Sctp
+ * Method:    on_network_in
+ * Signature: (J[B)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_sctp4j_Sctp_on_1network_1in
+  (JNIEnv *, jclass, jlong, jbyteArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/org/jitsi/sctp4j/DirectLink.java b/src/org/jitsi/sctp4j/DirectLink.java
new file mode 100644
index 0000000000000000000000000000000000000000..5498b0e36d1026f5011c398d46bde22542b902bb
--- /dev/null
+++ b/src/org/jitsi/sctp4j/DirectLink.java
@@ -0,0 +1,48 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+/**
+ * A direct connection that passes packets between two <tt>SctpSocket</tt>
+ * instances.
+ *
+ * @author Pawel Domas
+ */
+public class DirectLink
+    implements NetworkLink
+{
+    /**
+     * Instance "a" of this direct connection.
+     */
+    private final SctpSocket a;
+
+    /**
+     * Instance "b" of this direct connection.
+     */
+    private final SctpSocket b;
+    
+    public DirectLink(SctpSocket a, SctpSocket b)
+    {
+        this.a = a;
+        this.b = b;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void onConnOut(final SctpSocket s, final byte[] packet)
+    {
+        final SctpSocket dest = s == this.a ? this.b : this.a;
+        new Thread(new Runnable()
+        {
+            public void run()
+            {
+                dest.onConnIn(packet);
+            }
+        }).start();
+    }
+}
diff --git a/src/org/jitsi/sctp4j/NetworkLink.java b/src/org/jitsi/sctp4j/NetworkLink.java
new file mode 100644
index 0000000000000000000000000000000000000000..54a117c21f1aaca1c5d7c7608f2de4ab57e34f5a
--- /dev/null
+++ b/src/org/jitsi/sctp4j/NetworkLink.java
@@ -0,0 +1,26 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+/**
+ * Interface used by {@link SctpSocket} for sending network packets.
+ *
+ * FIXME: introduce offset and length parameters in order to be able to
+ *        re-use single buffer instance
+ *
+ * @author Pawel Domas
+ */
+public interface NetworkLink
+{
+    /**
+     * Callback triggered by <tt>SctpSocket</tt> whenever it wants to send some
+     * network packet.
+     * @param s source <tt>SctpSocket</tt> instance.
+     * @param packet network packet buffer.
+     */
+    public void onConnOut(final SctpSocket s, final byte[] packet);
+}
diff --git a/src/org/jitsi/sctp4j/SampleClient.java b/src/org/jitsi/sctp4j/SampleClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbe4ce50b747a02b9a16351994a07317b54ee973
--- /dev/null
+++ b/src/org/jitsi/sctp4j/SampleClient.java
@@ -0,0 +1,56 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+import org.jitsi.util.*;
+
+/**
+ * Sample SCTP client that uses UDP socket for transfers.
+ *
+ * @author Pawel Domas
+ */
+public class SampleClient
+{
+    /**
+     * The logger.
+     */
+    private final static Logger logger = Logger.getLogger(SampleClient.class);
+
+    public static void main(String[] args) throws Exception
+    {
+        String localAddr = "127.0.0.1";
+        int localPort = 48002;
+        int localSctpPort = 5002;
+
+        String remoteAddr = "127.0.0.1";
+        int remotePort = 48001;
+        int remoteSctpPort = 5001;
+
+        Sctp.init(0);
+
+        final SctpSocket client = Sctp.createSocket(localSctpPort);
+
+        UdpLink link
+            = new UdpLink(
+                client, localAddr, localPort, remoteAddr, remotePort);
+
+        client.setLink(link);
+
+        client.connect(remoteSctpPort);
+                    
+        try { Thread.sleep(1000); } catch(Exception e) { }
+                    
+        int sent = client.send(new byte[200], false, 0, 0);
+        logger.info("Client sent: "+sent);
+        
+        Thread.sleep(4000);
+        
+        client.close();
+        
+        Sctp.finish();
+    }
+}
diff --git a/src/org/jitsi/sctp4j/SampleLoop.java b/src/org/jitsi/sctp4j/SampleLoop.java
new file mode 100644
index 0000000000000000000000000000000000000000..1314d27ee33a481289877c73fa763334512bb69f
--- /dev/null
+++ b/src/org/jitsi/sctp4j/SampleLoop.java
@@ -0,0 +1,82 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+import org.jitsi.util.*;
+
+/**
+ * Sample that uses two <tt>SctpSocket</tt>s with {@link DirectLink}.
+ *
+ * @author Pawel Domas
+ */
+public class SampleLoop
+{
+    /**
+     * The logger.
+     */
+    private final static Logger logger = Logger.getLogger(SampleLoop.class);
+
+    public static void main(String[] args) throws Exception
+    {
+        Sctp.init(0);
+
+        final SctpSocket server = Sctp.createSocket(5001);
+        final SctpSocket client = Sctp.createSocket(5002);
+        
+        DirectLink link = new DirectLink(server, client);
+        server.setLink(link);
+        client.setLink(link);
+        
+        // Make server passive
+        server.listen();
+
+        // Client thread
+        new Thread(
+          new Runnable()
+          {
+            public void run()
+            {
+                if(!client.connect(server.getPort()))
+                {
+                    // FIXME: Unknown error returned on Windows,
+                    //        but it works after that
+                    //return;
+                }
+
+                logger.info("Client: connect");
+                    
+                try { Thread.sleep(1000); } catch(Exception e) { }
+                    
+                int sent = client.send(new byte[200], false, 0, 0);
+                logger.info("Client sent: " + sent);
+            }
+          }
+        ).start();
+
+        server.setDataCallback(
+            new SctpDataCallback()
+            {
+                @Override
+                public void onSctpPacket(byte[] data, int sid, int ssn, int tsn,
+                                         long ppid,
+                                         int context, int flags)
+                {
+                    logger.info("Server got some data: " + data.length
+                                + " stream: " + sid
+                                + " payload protocol id: " + ppid);
+                }
+            }
+        );
+
+        Thread.sleep(5*1000);
+        
+        server.close();
+        client.close();
+        
+        Sctp.finish();
+    }
+}
diff --git a/src/org/jitsi/sctp4j/SampleServer.java b/src/org/jitsi/sctp4j/SampleServer.java
new file mode 100644
index 0000000000000000000000000000000000000000..d38ddc114e535a6e466ab210eadca0aadbffee3f
--- /dev/null
+++ b/src/org/jitsi/sctp4j/SampleServer.java
@@ -0,0 +1,66 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+import org.jitsi.util.*;
+
+/**
+ * Sample SCTP server that uses UDP socket for transfers.
+ *
+ * @author Pawel Domas
+ */
+public class SampleServer
+{
+    /**
+     * The logger.
+     */
+    private final static Logger logger = Logger.getLogger(SampleServer.class);
+
+    public static void main(String[] args) throws Exception
+    {
+        String localAddr = "127.0.0.1";
+        int localPort = 48001;
+        int localSctpPort = 5001;
+
+        String remoteAddr = "127.0.0.1";
+        int remotePort = 48002;
+
+        Sctp.init(0);
+
+        final SctpSocket sock1 = Sctp.createSocket(localSctpPort);
+        
+        UdpLink link
+            = new UdpLink(
+                sock1, localAddr, localPort, remoteAddr, remotePort);
+
+        sock1.setLink(link);
+
+        sock1.listen();
+
+        sock1.accept();
+
+        sock1.setDataCallback(new SctpDataCallback()
+        {
+            @Override
+            public void onSctpPacket(byte[] data, int sid, int ssn, int tsn,
+                                     long ppid,
+                                     int context, int flags)
+            {
+                logger.info("Server got some data: " + data.length
+                                + " stream: " + sid
+                                + " payload protocol id: " + ppid);
+            }
+        });
+
+        Thread.sleep(40000);
+        
+        sock1.close();
+        
+        Sctp.finish();
+    }
+    
+}
diff --git a/src/org/jitsi/sctp4j/Sctp.java b/src/org/jitsi/sctp4j/Sctp.java
new file mode 100644
index 0000000000000000000000000000000000000000..097f029939fcbd99d50b9655c9b83aaaa656cc7d
--- /dev/null
+++ b/src/org/jitsi/sctp4j/Sctp.java
@@ -0,0 +1,344 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+import org.jitsi.util.*;
+
+import java.util.*;
+
+/**
+ * Class encapsulates native SCTP counterpart.
+ *
+ * @author Pawel Domas
+ */
+public class Sctp
+{
+    /**
+     * The logger.
+     */
+    private static final Logger logger = Logger.getLogger(Sctp.class);
+
+    static
+    {
+        String lib = "jnsctp";
+
+        try
+        {
+            System.loadLibrary(lib);
+        }
+        catch (Throwable t)
+        {
+            logger.error(
+                "Failed to load native library " + lib + ": " + t.getMessage());
+            throw new RuntimeException(t);
+        }
+    }
+
+    /**
+     * SCTP notification
+     */
+    public static final int MSG_NOTIFICATION = 0x2000;
+
+    /********  Notifications  **************/
+
+    /* notification types */
+    public static final int SCTP_ASSOC_CHANGE                = 0x0001;
+    public static final int SCTP_PEER_ADDR_CHANGE            = 0x0002;
+    public static final int SCTP_REMOTE_ERROR                = 0x0003;
+    public static final int SCTP_SEND_FAILED                 = 0x0004;
+    public static final int SCTP_SHUTDOWN_EVENT              = 0x0005;
+    public static final int SCTP_ADAPTATION_INDICATION       = 0x0006;
+    public static final int SCTP_PARTIAL_DELIVERY_EVENT      = 0x0007;
+    public static final int SCTP_AUTHENTICATION_EVENT        = 0x0008;
+    public static final int SCTP_STREAM_RESET_EVENT          = 0x0009;
+    public static final int SCTP_SENDER_DRY_EVENT            = 0x000a;
+    public static final int SCTP_NOTIFICATIONS_STOPPED_EVENT = 0x000b;
+    public static final int SCTP_ASSOC_RESET_EVENT           = 0x000c;
+    public static final int SCTP_STREAM_CHANGE_EVENT         = 0x000d;
+    public static final int SCTP_SEND_FAILED_EVENT           = 0x000e;
+
+    /* notification event structures */
+
+    /* association change event */
+    /*struct sctp_assoc_change {
+	    uint16_t sac_type;
+	    uint16_t sac_flags;
+	    uint32_t sac_length;
+	    uint16_t sac_state;
+	    uint16_t sac_error;
+	    uint16_t sac_outbound_streams;
+	    uint16_t sac_inbound_streams;
+	    sctp_assoc_t sac_assoc_id;
+	    uint8_t sac_info[]; // not available yet
+    };*/
+
+    /* sac_state values */
+    public static final int SCTP_COMM_UP        = 0x0001;
+    public static final int SCTP_COMM_LOST      = 0x0002;
+    public static final int SCTP_RESTART        = 0x0003;
+    public static final int SCTP_SHUTDOWN_COMP  = 0x0004;
+    public static final int SCTP_CANT_STR_ASSOC = 0x0005;
+
+    /* sac_info values */
+    public static final int SCTP_ASSOC_SUPPORTS_PR        = 0x01;
+    public static final int SCTP_ASSOC_SUPPORTS_AUTH      = 0x02;
+    public static final int SCTP_ASSOC_SUPPORTS_ASCONF    = 0x03;
+    public static final int SCTP_ASSOC_SUPPORTS_MULTIBUF  = 0x04;
+    public static final int SCTP_ASSOC_SUPPORTS_RE_CONFIG = 0x05;
+    public static final int SCTP_ASSOC_SUPPORTS_MAX       = 0x05;
+
+    /* Address event */
+    /*struct sctp_paddr_change {
+	    uint16_t spc_type;
+	    uint16_t spc_flags;
+	    uint32_t spc_length;
+	    struct sockaddr_storage spc_aaddr;
+	    uint32_t spc_state;
+	    uint32_t spc_error;
+	    sctp_assoc_t spc_assoc_id;
+	    uint8_t spc_padding[4];
+    };*/
+
+    /* paddr state values */
+    public static final int SCTP_ADDR_AVAILABLE   = 0x0001;
+    public static final int SCTP_ADDR_UNREACHABLE = 0x0002;
+    public static final int SCTP_ADDR_REMOVED     = 0x0003;
+    public static final int SCTP_ADDR_ADDED       = 0x0004;
+    public static final int SCTP_ADDR_MADE_PRIM   = 0x0005;
+    public static final int SCTP_ADDR_CONFIRMED   = 0x0006;
+
+    /* flags in stream_reset_event (strreset_flags) */
+    public static final int SCTP_STREAM_RESET_INCOMING_SSN = 0x0001;
+    public static final int SCTP_STREAM_RESET_OUTGOING_SSN = 0x0002;
+    public static final int SCTP_STREAM_RESET_DENIED       = 0x0004;
+    public static final int SCTP_STREAM_RESET_FAILED       = 0x0008;
+    public static final int SCTP_STREAM_CHANGED_DENIED     = 0x0010;
+
+    public static final int SCTP_STREAM_RESET_INCOMING     = 0x00000001;
+    public static final int SCTP_STREAM_RESET_OUTGOING     = 0x00000002;
+
+    /**
+     * Track initialization state of native counterpart.
+     */
+    private static boolean initialized;
+
+    /**
+     * Initializes native SCTP counterpart.
+     * @param port UDP encapsulation port.
+     */
+    public static synchronized void init(int port)
+    {
+        if(initialized)
+            return;
+
+        usrsctp_init(port);
+
+        initialized = true;
+    }
+
+    /**
+     * Initializes native SCTP counterpart.
+     * @param port UDP encapsulation port.
+     * @return <tt>true</tt> on success.
+     */
+    native private static boolean usrsctp_init(int port);
+
+    /**
+     * List of instantiated <tt>SctpSockets</tt> mapped by native pointer.
+     */
+    private static Map<Long, SctpSocket> sockets
+        = new HashMap<Long, SctpSocket>();
+
+    /**
+     * Creates new <tt>SctpSocket</tt> for given SCTP port. Allocates native
+     * resources bound to the socket.
+     * @param localPort local SCTP socket port.
+     * @return new <tt>SctpSocket</tt> for given SCTP port.
+     */
+    public static SctpSocket createSocket(int localPort)
+    {
+        Long ptr = usersctp_socket(localPort);
+        if(ptr != 0)
+        {
+            SctpSocket sock = new SctpSocket(ptr, localPort);
+            sockets.put(ptr, sock);            
+            return sock;
+        }
+        else
+            return null;
+    }
+
+    /**
+     * Creates native SCTP socket and returns pointer to it.
+     * @param localPort local SCTP socket port.
+     * @return native socket pointer or 0 if operation failed.
+     */
+    native private static long usersctp_socket(int localPort);
+
+    /**
+     * Sends given <tt>data</tt> on selected SCTP stream using given payload
+     * protocol identifier.
+     * FIXME: add offset and length buffer parameters.
+     * @param socketPtr native socket pointer.
+     * @param data the data to send.
+     * @param ordered should we care about message order ?
+     * @param sid SCTP stream identifier
+     * @param ppid payload protocol identifier
+     * @return sent bytes count or <tt>-1</tt> in case of an error.
+     */
+    native static int usrsctp_send(long socketPtr,  byte[] data,
+                                   boolean ordered, int sid, int ppid);
+
+    /**
+     * Makes socket passive.
+     * @param socketPtr native socket pointer.
+     */
+    native static void usrsctp_listen(long socketPtr);
+
+    /**
+     * Waits for incoming connection.
+     * @param socketPtr native socket pointer.
+     */
+    native static void usrsctp_accept(long socketPtr);
+
+    /**
+     * Connects SCTP socket to remote socket on given SCTP port.
+     * @param socketPtr native socket pointer.
+     * @param remotePort remote SCTP port.
+     * @return <tt>true</tt> if the socket has been successfully connected.
+     */
+    native static boolean usrsctp_connect(long socketPtr, int remotePort);
+
+    /**
+     * Closes SCTP socket addressed by given native pointer.
+     * @param ptr native socket pointer.
+     */
+    static void closeSocket(Long ptr)
+    {
+        usrsctp_close(ptr);
+    
+        sockets.remove(ptr);
+    }
+
+    /**
+     * Closes SCTP socket.
+     * @param socketPtr native socket pointer.
+     */
+    native private static void usrsctp_close(long socketPtr);
+
+    /**
+     * Disposes of the resources held by native counterpart.
+     */
+    public static synchronized void finish()
+    {
+        if(!initialized)
+            return;
+
+        try
+        {
+            // FIXME: fix this loop ?
+            // it comes from SCTP samples written in C
+            while(!usrsctp_finish())
+            {            
+                Thread.sleep(50);
+            }
+
+            initialized = false;
+        }
+        catch(InterruptedException e)
+        {
+            logger.error("Finish interrupted", e);
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    /**
+     * Disposes of the resources held by native counterpart.
+     * @return <tt>true</tt> if stack successfully released resources.
+     */
+    native private static boolean usrsctp_finish();
+
+    /**
+     * Method fired by native counterpart when SCTP stack wants to send
+     * network packet.
+     * @param socketAddr native socket pointer
+     * @param data buffer holding packet data
+     * @param tos type of service???
+     * @param set_df use IP don't fragment option
+     */
+    public static void onSctpOutboundPacket(long socketAddr, byte[] data,
+                                            int  tos,        int set_df)
+    {
+        // FIXME: handle tos and set_df
+
+        SctpSocket socket = sockets.get(socketAddr);
+        if(socket != null)
+        {
+            socket.onSctpOut(data, tos, set_df);
+        }
+        else
+        {
+            logger.error("No SctpSocket found for ptr: " + socketAddr);
+        }
+    }
+
+    /**
+     * Method fired by native counterpart to notify about incoming data.
+     *
+     * @param socketAddr native socket pointer
+     * @param data buffer holding received data
+     * @param sid stream id
+     * @param ssn
+     * @param tsn
+     * @param ppid payload protocol identifier
+     * @param context
+     * @param flags
+     */
+    public static void onSctpInboundPacket(long   socketAddr,
+                                           byte[] data,
+                                           int    sid,  int ssn,     int tsn,
+                                           long   ppid, int context, int flags)
+    {
+        SctpSocket socket = sockets.get(socketAddr);
+        if(socket != null)
+        {
+            socket.onSctpIn(data, sid, ssn, tsn, ppid, context, flags);
+        }
+        else
+        {
+            logger.error("No SctpSocket found for ptr: " + socketAddr);
+        }
+    }
+
+    /**
+     * Used by {@link SctpSocket} to pass received network packet to native
+     * counterpart.
+     *
+     * FIXME: add offset and length parameters
+     *
+     * @param socketPtr native socket pointer.
+     * @param packet network packet data.
+     */
+    static void onConnIn(long socketPtr, byte[] packet)
+    {
+        on_network_in(socketPtr, packet);
+    }
+
+    /**
+     * Passes network packet to native SCTP stack counterpart.
+     * @param socketPtr native socket pointer.
+     * @param packet buffer holding network packet data.
+     */
+    native private static void on_network_in(long socketPtr, byte[] packet);
+
+    /*
+    FIXME: to be added ?
+    int
+    usrsctp_shutdown(struct socket *so, int how);
+    */
+
+}
diff --git a/src/org/jitsi/sctp4j/SctpDataCallback.java b/src/org/jitsi/sctp4j/SctpDataCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5d9af5163ca1bb9f8ebd857e28bedd289ce64d6
--- /dev/null
+++ b/src/org/jitsi/sctp4j/SctpDataCallback.java
@@ -0,0 +1,28 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+/**
+ * Callback used to listen for incoming data on SCTP socket.
+ *
+ * @author Pawel Domas
+ */
+public interface SctpDataCallback
+{
+    /**
+     * Callback fired by <tt>SctpSocket</tt> to notify about incoming data.
+     * @param data buffer holding received data.
+     * @param sid SCTP stream identifier.
+     * @param ssn
+     * @param tsn
+     * @param ppid payload protocol identifier.
+     * @param context
+     * @param flags
+     */
+    void onSctpPacket(byte[] data, int sid, int ssn, int tsn, long ppid,
+                      int context, int flags);
+}
diff --git a/src/org/jitsi/sctp4j/SctpSocket.java b/src/org/jitsi/sctp4j/SctpSocket.java
new file mode 100644
index 0000000000000000000000000000000000000000..0518562e7b3ea5004b61c3e59335697165b870a2
--- /dev/null
+++ b/src/org/jitsi/sctp4j/SctpSocket.java
@@ -0,0 +1,354 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+import org.jitsi.util.*;
+
+/**
+ * SCTP socket implemented using "usrsctp" lib.
+ *
+ * @author Pawel Domas
+ */
+public class SctpSocket
+{
+    /**
+     * The logger.
+     */
+    private final static Logger logger = Logger.getLogger(SctpSocket.class);
+
+    /**
+     * Pointer to native socket counterpart.
+     */
+    long socketPtr;
+
+    /**
+     * Local SCTP port.
+     */
+    int localPort;
+
+    /**
+     * The link used to send network packets.
+     */
+    private NetworkLink link;
+
+    /**
+     * Callback used to notify about received data.
+     */
+    private SctpDataCallback dataCallback;
+
+    /**
+     * Creates new instance of <tt>SctpSocket</tt>.
+     * @param socketPtr native socket pointer.
+     * @param localPort local SCTP port on which this socket is bound.
+     */
+    SctpSocket(long socketPtr, int localPort)
+    {
+        this.socketPtr = socketPtr;
+        this.localPort = localPort;
+    }
+
+    /**
+     * Sets the link that will be used to send network packets.
+     * @param link <tt>NetworkLink</tt> that will be used by this instance to
+     *             send network packets.
+     */
+    public void setLink(NetworkLink link)
+    {
+        this.link = link;
+    }
+
+    /**
+     * Returns SCTP port used by this socket.
+     * @return SCTP port used by this socket.
+     */
+    public int getPort()
+    {
+        return this.localPort;
+    }
+
+    /**
+     * Makes SCTP socket passive.
+     */
+    public void listen()
+    {
+        Sctp.usrsctp_listen(socketPtr);
+    }
+
+    /**
+     * Accepts incoming SCTP connection.
+     */
+    public void accept()
+    {
+        Sctp.usrsctp_accept(socketPtr);
+    }
+
+    /**
+     * Initializes SCTP connection by sending INIT message.
+     * @param remotePort remote SCTP port.
+     * @return <tt>true</tt> on success.
+     */
+    public boolean connect(int remotePort)
+    {
+        return Sctp.usrsctp_connect(socketPtr, remotePort);
+    }
+
+    /**
+     * Sends given <tt>data</tt> on selected SCTP stream using given payload
+     * protocol identifier.
+     * FIXME: add offset and length buffer parameters.
+     * @param data the data to send.
+     * @param ordered should we care about message order ?
+     * @param sid SCTP stream identifier
+     * @param ppid payload protocol identifier
+     * @return sent bytes count or <tt>-1</tt> in case of an error.
+     */
+    public int send(byte[] data, boolean ordered, int sid, int ppid)
+    {
+        return Sctp.usrsctp_send(socketPtr, data, ordered, sid, ppid);
+    }
+
+    /**
+     * Callback triggered by Sctp stack whenever it wants to send some
+     * network packet.
+     * @param packet network packet buffer.
+     * @param tos type of service???
+     * @param set_df use IP don't fragment option
+     */
+    void onSctpOut(byte[] packet, int tos, int set_df)
+    {
+        this.link.onConnOut(this, packet);
+    }
+
+    /**
+     * Method fired by SCTP stack to notify about incoming data.
+     *
+     * @param data buffer holding received data
+     * @param sid stream id
+     * @param ssn
+     * @param tsn
+     * @param ppid payload protocol identifier
+     * @param context
+     * @param flags
+     */
+    void onSctpIn(byte[] data, int sid,     int ssn,  int tsn,
+                  long   ppid, int context, int flags)
+    {
+        if(dataCallback != null)
+        {
+            dataCallback.onSctpPacket(
+                data, sid, ssn, tsn, ppid, context, flags);
+        }
+    }
+
+    /**
+     * Sets the callback that will be fired when new data is received.
+     * @param callback the callback that will be fired when new data
+     *                 is received.
+     */
+    public void setDataCallback(SctpDataCallback callback)
+    {
+        this.dataCallback = callback;
+    }
+
+    /**
+     * Call this method to pass network packets received on the link.
+     * @param packet network packet received.
+     */
+    public void onConnIn(byte[] packet)
+    {
+        //debugSctpPacket(packet);
+
+        Sctp.onConnIn(socketPtr, packet);
+    }
+
+    /**
+     * Closes this socket. After call to this method this instance MUST NOT be
+     * used.
+     */
+    public void close()
+    {
+        if(socketPtr != 0)
+        {
+            Sctp.closeSocket(socketPtr);
+
+            socketPtr = 0;
+        }
+    }
+    
+    public synchronized static void debugSctpPacket(byte[] packet, String id)
+    {
+        System.out.println(id);
+        if(packet.length >= 12)
+        {
+            int i=0;
+            //Common header
+            int srcPort = bytes_to_short(packet, 0);
+            int dstPort = bytes_to_short(packet, 2);
+            
+            long verificationTag = bytes_to_long(packet, 4);
+            long checksum = bytes_to_long(packet, 8);
+            
+            logger.debug(
+              "SRC P: " + srcPort 
+              + " DST P: " + dstPort
+              + " VTAG: 0x" + Long.toHexString(verificationTag) 
+              + " CHK: 0x" + Long.toHexString(checksum));
+            
+            /*if(verificationTag == 0)
+            {
+                // This is init header
+                System.out.println("WE HAVE INIT!!!");
+            }*/
+            debugChunks(packet);
+        }
+    }
+    
+    static void debugChunks(byte[] packet)
+    {
+        int offset = 12;// After common header
+        while((packet.length-offset) >= 4)
+        {
+            int chunkType = packet[offset++] & 0xFF;
+            
+            int chunkFlags = packet[offset++] & 0xFF;
+            
+            int chunkLength = bytes_to_short(packet, offset);
+            offset+=2;
+            
+            logger.debug("CH: " + chunkType
+                             + " FL: " + chunkFlags
+                             + " L: "+chunkLength );
+            if(chunkType == 1)
+            {
+                //Init chunk info
+                
+                long initTag = bytes_to_long(packet, offset);
+                offset += 4;
+                
+                long a_rwnd = bytes_to_long(packet, offset);
+                offset += 4;
+                
+                int nOutStream = bytes_to_short(packet, offset);
+                offset += 2;
+                
+                int nInStream = bytes_to_short(packet, offset);
+                offset += 2;
+                
+                long initTSN = bytes_to_long(packet, offset);
+                offset += 4;
+                
+                logger.debug(
+                    "ITAG: 0x" + Long.toHexString(initTag)
+                    + " a_rwnd: " + a_rwnd
+                    + " nOutStream: " + nOutStream
+                    + " nInStream: " + nInStream
+                    + " initTSN: 0x" + Long.toHexString(initTSN));
+
+                // Parse Type-Length-Value chunks
+                /*while(offset < chunkLength)
+                {
+                    //System.out.println(packet[offset++]&0xFF);
+                    int type = bytes_to_short(packet, offset);
+                    offset += 2;
+                    
+                    int length = bytes_to_short(packet, offset);
+                    offset += 2;
+                    
+                    // value
+                    offset += (length-4);
+                    System.out.println(
+                        "T: "+type+" L: "+length+" left: "+(chunkLength-offset));
+                }*/
+
+                offset += (chunkLength-4-16);
+            }
+            else if(chunkType == 0)
+            {
+                // Payload
+                boolean U = (chunkFlags & 0x4) > 0;
+                boolean B = (chunkFlags & 0x2) > 0;
+                boolean E = (chunkFlags & 0x1) > 0;
+                
+                long TSN = bytes_to_long(packet, offset); offset += 4;
+
+                int streamIdS = bytes_to_short(packet, offset); offset += 2;
+                
+                int streamSeq = bytes_to_short(packet, offset); offset += 2;
+                
+                long PPID = bytes_to_long(packet, offset); offset += 4;
+                
+                logger.debug(
+                    "U: " + U + " B: " +B + " E: " + E 
+                    + " TSN: 0x" + Long.toHexString(TSN)
+                    + " SID: 0x" + Integer.toHexString(streamIdS)
+                    + " SSEQ: 0x" + Integer.toHexString(streamSeq)
+                    + " PPID: 0x" + Long.toHexString(PPID)
+                );
+
+                offset += (chunkLength-4-12);
+            }
+            else if(chunkType == 6)
+            {
+                // Abort
+                logger.debug("We have abort!!!");
+
+                if(offset >= chunkLength)
+                    logger.debug("No abort CAUSE!!!");
+                    
+                while(offset < chunkLength)
+                {
+                    int causeCode = bytes_to_short(packet, offset);
+                    offset += 2;
+                    
+                    int causeLength = bytes_to_short(packet, offset);
+                    offset += 2;
+                    
+                    logger.debug("Cause: " + causeCode + " L: " + causeLength);
+                }
+            }
+            else
+            {
+                offset += (chunkLength-4);
+            }
+        }
+    }
+    /**
+     * Reads 32 bit unsigned int from the buffer at specified offset
+     *
+     * @param buffer
+     * @param offset
+     * @return 32 bit unsigned value
+     */
+    private static long bytes_to_long(byte[] buffer, int offset)
+    {
+        int fByte = (0x000000FF & ((int) buffer[offset]));
+        int sByte = (0x000000FF & ((int) buffer[offset + 1]));
+        int tByte = (0x000000FF & ((int) buffer[offset + 2]));
+        int foByte = (0x000000FF & ((int) buffer[offset + 3]));
+        return ((long) (fByte << 24
+            | sByte << 16
+            | tByte << 8
+            | foByte))
+            & 0xFFFFFFFFL;
+    }
+
+    /**
+     * Reads 16 bit unsigned int from the buffer at specified offset
+     *
+     * @param buffer
+     * @param offset
+     * @return 16 bit unsigned int
+     */
+    private static int bytes_to_short(byte[] buffer, int offset)
+    {
+        int fByte = (0x000000FF & ((int) buffer[offset]));
+        int sByte = (0x000000FF & ((int) buffer[offset + 1]));
+        return ((fByte << 8
+            | sByte))
+            & 0xFFFF;
+    }
+}
diff --git a/src/org/jitsi/sctp4j/UdpLink.java b/src/org/jitsi/sctp4j/UdpLink.java
new file mode 100644
index 0000000000000000000000000000000000000000..337cf1bbd5349e3029baefc6d784c5970c785882
--- /dev/null
+++ b/src/org/jitsi/sctp4j/UdpLink.java
@@ -0,0 +1,121 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.sctp4j;
+
+import org.jitsi.util.*;
+
+import java.net.*;
+import java.io.*;
+
+/**
+ * Class used in code samples to send SCTP packets through UDP sockets.
+ *
+ * FIXME: fix receiving loop
+ *
+ * @author Pawel Domas
+ */
+public class UdpLink
+    implements NetworkLink
+{
+    /**
+     * The logger
+     */
+    private final static Logger logger = Logger.getLogger(UdpLink.class);
+
+    /**
+     * <tt>SctpSocket</tt> instance that is used in this connection.
+     */
+    private final SctpSocket sctpSocket;
+
+    /**
+     * Udp socket used for transport.
+     */
+    private final DatagramSocket udpSocket;
+
+    /**
+     * Destination UDP port.
+     */
+    private final int remotePort;
+
+    /**
+     * Destination <tt>InetAddress</tt>.
+     */
+    private final InetAddress remoteIp;
+
+    /**
+     * Creates new instance of <tt>UdpConnection</tt>.
+     *
+     * @param sctpSocket SCTP socket instance used by this connection.
+     * @param localIp local IP address.
+     * @param localPort local UDP port.
+     * @param remoteIp remote address.
+     * @param remotePort destination UDP port.
+     * @throws IOException when we fail to resolve any of addresses
+     *                     or when opening UDP socket.
+     */
+    public UdpLink(SctpSocket sctpSocket,
+                   String localIp, int localPort,
+                   String remoteIp, int remotePort)
+        throws IOException
+    {
+        this.sctpSocket = sctpSocket;
+        
+        this.udpSocket
+            = new DatagramSocket(localPort, InetAddress.getByName(localIp));
+
+        this.remotePort = remotePort;
+        this.remoteIp = InetAddress.getByName(remoteIp);
+        
+        // Listening thread
+        new Thread(
+            new Runnable()
+            {
+                public void run()
+                {
+                    try
+                    {
+                        byte[] buff = new byte[2048];
+                        DatagramPacket p = new DatagramPacket(buff, 2048);
+                        while(true)
+                        {
+                            udpSocket.receive(p);
+                            int len = p.getLength();
+                            byte[] packet = new byte[len];
+                            System.arraycopy(buff, 0, packet, 0, len);
+                            UdpLink.this.sctpSocket.onConnIn(packet);
+                        }
+                    }
+                    catch(IOException e)
+                    {
+                        logger.error(e, e);
+                    }
+                }    
+            }
+        ).start();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onConnOut(final SctpSocket s, final byte[] packetData)
+    {
+        try
+        {
+            DatagramPacket packet 
+                = new DatagramPacket( packetData,
+                                      packetData.length,
+                                      remoteIp,
+                                      remotePort);
+            udpSocket.send(packet);
+        }
+        catch(IOException e)
+        {
+            logger.error("Error while trying to send SCTP packet", e);
+        }
+    }
+}