diff for duplicates of <49f712b3b6704c3096d3834a5985a4d6@AcuMS.aculab.com>
diff --git a/a/content_digest b/N1/content_digest
index 444140f..a7e011d 100644
--- a/a/content_digest
+++ b/N1/content_digest
@@ -46,19 +46,7 @@
" linux-rdma\@vger.kernel.org <linux-rdma\@vger.kernel.org>",
" cluster-devel\@redhat.com <cluster-devel\@redhat.com>",
" Jakub Kicinski <kuba\@kernel.org>",
- " linux-block\@vger.kernel.org <linux-block\@vger.kernel.org>",
- " Alexey Kuznetsov <kuznet\@ms2.inr.ac.ru>",
- " ceph-devel\@vger.kernel.org <ceph-devel\@vger.kernel.org>",
- " linux-nfs\@vger.kernel.org <linux-nfs\@vger.kernel.org>",
- " Neil Horman <nhorman\@tuxdriver.com>",
- " Hideaki YOSHIFUJI <yoshfuji\@linux-ipv6.org>",
- " netdev\@vger.kernel.org <netdev\@vger.kernel.org>",
- " Vlad Yasevich <vyasevich\@gmail.com>",
- " Eric Dumazet <edumazet\@google.com>",
- " Jon Maloy <jmaloy\@redhat.com>",
- " Ying Xue <ying.xue\@windriver.com>",
- " David S. Miller <davem\@davemloft.net>",
- " ocfs2-devel\@oss.oracle.com <ocfs2-devel\@oss.oracle.com>\0"
+ " linux-block\@vger.kernel\0"
]
[
"\0001:1\0"
@@ -1508,4 +1496,4 @@
"}"
]
-049f0d794ae7b86fe86338ebd5f3cc0175a0cd041f88b4d1c5c988d6b0c24e31
+507e80745dbe531a49a0ba9361ff793c24ea8ec032b159c64a76215b95da0788
diff --git a/N2/3.hdr b/N2/3.hdr
new file mode 100644
index 0000000..4b86001
--- /dev/null
+++ b/N2/3.hdr
@@ -0,0 +1,4 @@
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
diff --git a/N2/3.txt b/N2/3.txt
new file mode 100644
index 0000000..89bf1cb
--- /dev/null
+++ b/N2/3.txt
@@ -0,0 +1,4 @@
+_______________________________________________
+linux-nvme mailing list
+linux-nvme@lists.infradead.org
+http://lists.infradead.org/mailman/listinfo/linux-nvme
\ No newline at end of file
diff --git a/a/content_digest b/N2/content_digest
index 444140f..084572e 100644
--- a/a/content_digest
+++ b/N2/content_digest
@@ -32,11 +32,10 @@
"To\0'Matthew Wilcox' <willy\@infradead.org>\0"
]
[
- "Cc\0'David Howells' <dhowells\@redhat.com>",
- " Christoph Hellwig <hch\@lst.de>",
- " Marcelo Ricardo Leitner <marcelo.leitner\@gmail.com>",
+ "Cc\0Marcelo Ricardo Leitner <marcelo.leitner\@gmail.com>",
+ " Eric Dumazet <edumazet\@google.com>",
" linux-nvme\@lists.infradead.org <linux-nvme\@lists.infradead.org>",
- " linux-kernel\@vger.kernel.org <linux-kernel\@vger.kernel.org>",
+ " 'David Howells' <dhowells\@redhat.com>",
" linux-sctp\@vger.kernel.org <linux-sctp\@vger.kernel.org>",
" target-devel\@vger.kernel.org <target-devel\@vger.kernel.org>",
" linux-afs\@lists.infradead.org <linux-afs\@lists.infradead.org>",
@@ -44,17 +43,18 @@
" linux-cifs\@vger.kernel.org <linux-cifs\@vger.kernel.org>",
" rds-devel\@oss.oracle.com <rds-devel\@oss.oracle.com>",
" linux-rdma\@vger.kernel.org <linux-rdma\@vger.kernel.org>",
+ " Christoph Hellwig <hch\@lst.de>",
" cluster-devel\@redhat.com <cluster-devel\@redhat.com>",
- " Jakub Kicinski <kuba\@kernel.org>",
- " linux-block\@vger.kernel.org <linux-block\@vger.kernel.org>",
" Alexey Kuznetsov <kuznet\@ms2.inr.ac.ru>",
+ " linux-block\@vger.kernel.org <linux-block\@vger.kernel.org>",
+ " Jakub Kicinski <kuba\@kernel.org>",
" ceph-devel\@vger.kernel.org <ceph-devel\@vger.kernel.org>",
" linux-nfs\@vger.kernel.org <linux-nfs\@vger.kernel.org>",
" Neil Horman <nhorman\@tuxdriver.com>",
" Hideaki YOSHIFUJI <yoshfuji\@linux-ipv6.org>",
" netdev\@vger.kernel.org <netdev\@vger.kernel.org>",
" Vlad Yasevich <vyasevich\@gmail.com>",
- " Eric Dumazet <edumazet\@google.com>",
+ " linux-kernel\@vger.kernel.org <linux-kernel\@vger.kernel.org>",
" Jon Maloy <jmaloy\@redhat.com>",
" Ying Xue <ying.xue\@windriver.com>",
" David S. Miller <davem\@davemloft.net>",
@@ -1507,5 +1507,17 @@
" return CMSG_LEN(sizeof *sinfo);\n",
"}"
]
+[
+ "\0001:3\0"
+]
+[
+ "b\0"
+]
+[
+ "_______________________________________________\n",
+ "linux-nvme mailing list\n",
+ "linux-nvme\@lists.infradead.org\n",
+ "http://lists.infradead.org/mailman/listinfo/linux-nvme"
+]
-049f0d794ae7b86fe86338ebd5f3cc0175a0cd041f88b4d1c5c988d6b0c24e31
+cd8f30d9e0ee4a699110931264ee6f9729c4fcb45b0091bcee6ac4fe920f8936
diff --git a/a/1.txt b/N3/1.txt
index ca5eed1..a418302 100644
--- a/a/1.txt
+++ b/N3/1.txt
@@ -41,4 +41,1386 @@ to support different and broken kernel releases.
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
-Registration No: 1397386 (Wales)
\ No newline at end of file
+Registration No: 1397386 (Wales)
+-------------- next part --------------
+#ident "@(#) (c) Aculab plc $Header: /home/cvs/repository/ss7/stack/src/driver/linux/ss7osglue.c,v 1.157 2019-08-29 16:09:14 davidla Exp $ $Name: $"
+#ifndef MODULE
+#define MODULE
+#endif
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+#error minimum kernel version is 2.6.28
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+#include <generated/autoconf.h>
+#else
+#include <linux/autoconf.h>
+#endif
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kmod.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/signal.h>
+#endif
+#include <linux/wait.h>
+#include <linux/socket.h>
+#include <linux/signal.h>
+#include <linux/poll.h>
+#include <linux/net.h>
+#include <linux/nsproxy.h>
+#include <linux/in.h>
+#include <linux/reboot.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include <linux/kthread.h>
+
+/* This is only in the kernel build tree */
+#include <net/sock.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+#include <uapi/linux/sctp.h>
+#else
+#include <net/sctp/user.h> /* netinet/sctp.h ought to be this file */
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
+#define wait_queue_head __wait_queue_head
+#define wait_queue_entry __wait_queue
+#endif
+
+#define SK_PROTOCOL(sock) (sock)->sk->sk_protocol
+
+extern void ss7_trace_mem(int, void *, int, const char *, ...);
+extern void ss7_trace_printf(int, const char *, ...);
+
+/* Aculab DACP interfaces - these are in aculab's kern_if.h */
+void *dacp_symbol_get(const char *);
+int dacp_symbol_release(const char *);
+
+MODULE_AUTHOR("Aculab");
+MODULE_LICENSE("Proprietary");
+
+#include "ss7osglue.h"
+
+/* Mutex for driver interface code */
+static struct mutex ss7_glue_mutex;
+
+static int ss7dev_major;
+static const void *ss7_dtls_handle;
+static int ss7_use_count;
+static int ss7_stop_pid;
+
+static struct task_struct *asserted_tasks[16];
+static unsigned int asserted_task_count;
+
+typedef char ss7_verify_const[ SS7_SOCK_STREAM == SOCK_STREAM && SS7_SOCK_SEQPACKET == SOCK_SEQPACKET ? 1 : -1];
+
+static void ss7_net_ns_unload(void);
+
+#define TCP_NODELAY 1
+
+static int ss7_glue_open(struct inode *, struct file *);
+static int ss7_glue_release(struct inode *, struct file *);
+static long ss7_glue_unlocked_ioctl(struct file *, unsigned int, unsigned long);
+static unsigned int ss7_glue_poll(struct file *const, poll_table *);
+
+static struct file_operations ss7dev_fop =
+{
+ open: ss7_glue_open,
+ release: ss7_glue_release,
+ unlocked_ioctl: ss7_glue_unlocked_ioctl,
+ compat_ioctl: ss7_glue_unlocked_ioctl,
+ poll: ss7_glue_poll,
+ owner: THIS_MODULE
+};
+
+static int ss7_reboot_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ /* System being rebooted.
+ * I added this hoping to use it to get the ss7maint daemon to exit,
+ * but it isn't called until all user processes have died.
+ * Leave it here - might be useful one day. */
+ return 0;
+}
+
+static struct notifier_block ss7_reboot_notifier_block = {
+ .notifier_call = ss7_reboot_notify,
+};
+
+static int
+ss7_init_fail(int rval)
+{
+ if (ss7dev_major > 0)
+ unregister_chrdev(ss7dev_major, "ss7server");
+ return rval;
+}
+
+static int
+ss7_init_mod(void)
+{
+ const void *(*dtls_register)(const char *, int (*)(struct dtls_get_if *));
+ int rval;
+
+ ss7_mutex_init(&ss7_glue_mutex);
+
+ printk(KERN_INFO "%s\n", ss7version);
+
+ ss7dev_major = register_chrdev(0, "ss7server", &ss7dev_fop);
+
+ if (ss7dev_major < 0) {
+ printk(KERN_INFO "ss7server: register_chrdev() failed: %d\n",
+ ss7dev_major);
+ return ss7_init_fail(ss7dev_major);
+ }
+
+ rval = ss7_driver_init();
+ if (rval != 0) {
+ printk(KERN_INFO "ss7server: ss7_driver_init() failed: %d\n", rval);
+ return ss7_init_fail(-EIO);
+ }
+
+ dtls_register = dacp_symbol_get("acuc_dtls_register");
+ if (dtls_register == NULL)
+ printk(KERN_INFO "ss7server: cannot locate \"acuc_dtls_register\"\n");
+ else
+ ss7_dtls_handle = dtls_register(DYNAMIC_TLS_PREFIX "ss7",
+ ss7_tls_get_if);
+
+ register_reboot_notifier(&ss7_reboot_notifier_block);
+ return 0;
+}
+
+static void
+ss7_cleanup_mod(void)
+{
+ int (*dtls_unregister)(const void *);
+
+ unregister_reboot_notifier(&ss7_reboot_notifier_block);
+
+ if (ss7_dtls_handle != NULL) {
+ dtls_unregister = dacp_symbol_get("acuc_dtls_unregister");
+ dacp_symbol_release("acuc_dtls_register");
+ if (dtls_unregister != NULL) {
+ dtls_unregister(ss7_dtls_handle);
+ dacp_symbol_release("acuc_dtls_unregister");
+ }
+ }
+
+ ss7_init_fail(0);
+
+ printk(KERN_INFO "Aculab ss7server: driver unloaded\n");
+}
+
+module_init(ss7_init_mod)
+module_exit(ss7_cleanup_mod)
+
+static int
+ss7_glue_open(struct inode *const inode, struct file *const filp)
+{
+ int rval, pid;
+
+ if (filp->private_data)
+ /* Duplicate open */
+ return 0;
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ if (ss7_use_count < 0) {
+ /* ss7_driver_shutdown() has been called, to late to do anything */
+ ss7_mutex_exit(&ss7_glue_mutex);
+ return -EIO;
+ }
+ ss7_use_count++;
+ ss7_mutex_exit(&ss7_glue_mutex);
+
+ rval = ss7_devif_open(&filp->private_data);
+ if (rval != 0) {
+ ss7_mutex_enter(&ss7_glue_mutex);
+ ss7_use_count--;
+ ss7_mutex_exit(&ss7_glue_mutex);
+ pid = ss7_pid();
+ if (pid != ss7_stop_pid)
+ printk(KERN_INFO "ss7_devif_open() pid %d failed ss7 error %d\n",
+ pid, rval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+ss7_glue_release(struct inode *const inode, struct file *const filp)
+{
+ if (filp->private_data)
+ ss7_devif_close(filp->private_data);
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ ss7_use_count--;
+
+ if (ss7_use_count == 0 && ss7_stop_pid != 0) {
+ /* Last user process has gone, complete shutdown functions */
+ ss7_net_ns_unload();
+ /* Stop any more opens */
+ ss7_use_count = -1;
+ ss7_driver_shutdown();
+ }
+
+ ss7_mutex_exit(&ss7_glue_mutex);
+
+ return 0;
+}
+
+static long
+ss7_glue_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ if (!filp->private_data)
+ return -ENODEV;
+
+ switch (cmd) {
+
+ case SS7_STOP: /* ss7maint shutting us down */
+ /* Start shutdown now, will complete on last close */
+ ss7_driver_stop();
+ ss7_stop_pid = ss7_pid();
+ return 0;
+
+ /* Request from ss7maint or user application */
+ case SS7_USER_IOCTL_CODE:
+ return ss7dev_ioctl(filp->private_data, cmd, arg);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static unsigned int
+ss7_glue_poll(struct file *filp, poll_table *pt)
+{
+ poll_wait(filp, *ss7_devif_get_pollqueue_head(filp->private_data), pt);
+ return ss7_devif_get_poll_status(filp->private_data);
+}
+
+void *
+ss7_os_malloc(int s, int ss7_flags)
+{
+ return kmalloc(s, GFP_KERNEL);
+}
+
+void
+ss7_os_free(void *p)
+{
+ kfree(p);
+}
+
+void
+ss7_poll_queue_head_deinit(wait_queue_head_t **pqhp)
+{
+ ss7_os_free(*pqhp);
+}
+
+int
+ss7_poll_queue_head_init(wait_queue_head_t **pqhp)
+{
+ wait_queue_head_t *pqh = ss7_os_malloc(sizeof *pqh, 0);
+ if (pqh == NULL)
+ return -1;
+ init_waitqueue_head(pqh);
+ *pqhp = pqh;
+ return 0;
+}
+
+void
+ss7_pollwakeup(wait_queue_head_t **pqh, unsigned int poll_event)
+{
+ wake_up(*pqh);
+}
+
+void
+ss7_kill_task(struct task_struct *task, int signo)
+{
+ /* Send signal even though set to SIG_IGN */
+ force_sig(signo, task);
+}
+
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
+/* spinlock_t is a typedef for an unnamed structure so we can't
+ * make 'struct spinlock' match the kernel spinlock type. */
+#define SPINLOCK_CAST (spinlock_t *)
+#else
+#define SPINLOCK_CAST
+#endif
+
+size_t
+ss7_spin_lock_size(void)
+{
+ return sizeof *SPINLOCK_CAST(struct spinlock *)0;
+}
+
+void
+ss7_spin_lock_init(struct spinlock *s)
+{
+ spin_lock_init(SPINLOCK_CAST s);
+}
+
+void
+ss7_spin_lock_enter(struct spinlock *s)
+{
+ spin_lock(SPINLOCK_CAST s);
+}
+
+void
+ss7_spin_lock_exit(struct spinlock *s)
+{
+ spin_unlock(SPINLOCK_CAST s);
+}
+
+size_t
+ss7_mutex_size(void)
+{
+ return sizeof(struct mutex);
+}
+
+void
+ss7_mutex_init(struct mutex *s)
+{
+ mutex_init(s);
+}
+
+void
+ss7_mutex_enter(struct mutex *s)
+{
+ mutex_lock(s);
+}
+
+int
+ss7_mutex_enter_tmo(struct mutex *s, int max_wait)
+{
+ /* There is no mutex_enter_timeout() however this was all added
+ * to stop status commands sleeping forever when a process has
+ * 'oopsed' with a mutex held.
+ * Do a sneak check on the state of any owning task then
+ * wait interruptibly.
+ * ^C should error out the status call. */
+
+ /* If uncontended just acquire */
+ if (mutex_trylock(s))
+ return 1;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
+ {
+ struct task_struct *owner;
+ int state;
+
+ spin_lock(&s->wait_lock);
+ owner = __mutex_owner(s);
+ state = owner ? owner->state : 0;
+ spin_unlock(&s->wait_lock);
+ if (state & TASK_DEAD)
+ /* mutex will never be released, treat as timeout */
+ return 0;
+ }
+#endif
+
+ /* If C7_ASSERT() has been called, just let everyone in */
+ if (asserted_task_count)
+ return 0;
+
+ return mutex_lock_interruptible(s) ? -1 /* EINTR */ : 1 /* acquired */;
+}
+
+void
+ss7_mutex_exit(struct mutex *s)
+{
+ mutex_unlock(s);
+}
+
+size_t
+ss7_cv_size(void)
+{
+ return sizeof(wait_queue_head_t);
+}
+
+void
+ss7_cv_init(wait_queue_head_t *const v)
+{
+ init_waitqueue_head(v);
+}
+
+static int
+ss7_schedule_tmo(int tmo_ms)
+{
+ int tmo_jiffies;
+
+ /* Really sleep - unless woken since unlocking spinlock */
+ if (tmo_ms >= 0) {
+ if (tmo_ms <= 1)
+ tmo_jiffies = tmo_ms;
+ else
+ /* Convert to jiffies and round up */
+ tmo_jiffies = 1 + (tmo_ms + 1 - 1) * 16 / (16000/HZ);
+ /* Return value of schedule_timeout() is unexpired timeout */
+ /* We want 0 for 'timedout' (to match cv_wait_sig()) */
+ return schedule_timeout(tmo_jiffies) != 0;
+ }
+
+ schedule();
+ if (!signal_pending(current))
+ /* Woken by the event */
+ return 1;
+
+ /* Report 0 for a signal, except -1 for SIGKILL (reboot) */
+ return sigismember(¤t->pending.signal, SIGKILL) ? -1 : 0;
+}
+
+int
+ss7_cv_wait_guts(wait_queue_head_t *cvp, struct mutex *mtxp,
+ int interruptible, int tmo_ms)
+{
+ int r;
+ struct wait_queue_entry w;
+ int sleep_state;
+
+ init_waitqueue_entry(&w, current);
+
+ /* Tell scheduler we are going to sleep... */
+ if (signal_pending(current) && !interruptible)
+ /* We don't want waking immediately (again) */
+ sleep_state = TASK_UNINTERRUPTIBLE;
+ else
+ sleep_state = TASK_INTERRUPTIBLE;
+ set_current_state(sleep_state);
+
+ /* Connect to condition variable ... */
+ add_wait_queue(cvp, &w);
+ mutex_unlock(mtxp); /* Release mutex */
+
+ r = ss7_schedule_tmo(tmo_ms);
+
+ /* Disconnect from condition variable ... */
+ remove_wait_queue(cvp, &w);
+
+ /* Re-acquire mutex */
+ mutex_lock(mtxp);
+
+ /* return 1 if woken, 0 if timed_out/signal, -1 if SIGKILL */
+ return r;
+}
+
+int
+ss7_cv_wait_spin_lock(wait_queue_head_t *cvp, struct spinlock *lock,
+ int interruptible, int tmo_ms)
+{
+ int r;
+ struct wait_queue_entry w;
+ int sleep_state;
+
+ init_waitqueue_entry(&w, current);
+
+ /* Tell scheduler we are going to sleep... */
+ if (signal_pending(current) && !interruptible)
+ /* We don't want waking immediately (again) */
+ sleep_state = TASK_UNINTERRUPTIBLE;
+ else
+ sleep_state = TASK_INTERRUPTIBLE;
+ set_current_state(sleep_state);
+
+ /* Connect to condition variable ... */
+ add_wait_queue(cvp, &w);
+ spin_unlock(SPINLOCK_CAST lock);
+
+ r = ss7_schedule_tmo(tmo_ms);
+
+ /* Disconnect from condition variable ... */
+ remove_wait_queue(cvp, &w);
+
+ /* Re-acquire mutex */
+ spin_lock(SPINLOCK_CAST lock);
+
+ return r;
+}
+
+/*---------------------------------------------------------------------**
+** ss7_cv_broadcast **
+** Awaken all threads that are sleeping on a condition variable. **
+** Caller must use the associated mutex sensibly, i.e. ... **
+** acquire the mutex **
+** Set some flag that a sleeping thread will check for **
+** ss7_cv_broadcast() **
+** release the mutex **
+**---------------------------------------------------------------------*/
+
+void
+ss7_cv_broadcast(wait_queue_head_t *const cvp)
+{
+ wake_up(cvp);
+}
+
+
+unsigned long
+ss7_copy_to_user(void *to, const void *from, unsigned long c)
+{
+ return copy_to_user(to, from, c);
+}
+
+unsigned long
+ss7_copy_from_user(void *to, const void *from, unsigned long c)
+{
+ return copy_from_user(to, from, c);
+}
+
+unsigned int
+ss7_pid(void)
+{
+ return current->pid;
+}
+
+struct task_struct *
+ss7_current_task(void)
+{
+ return current;
+}
+
+unsigned int
+ss7_task_pid(struct task_struct *task)
+{
+ return task->pid;
+}
+
+int
+ss7_glue_thread_fn(void *ss7_thread)
+{
+ ss7_thread_run(ss7_thread);
+ module_put_and_exit(0);
+ return 0;
+}
+
+struct task_struct *
+ss7_os_thread_create(struct ss7_thread *thrp, const char *desc)
+{
+ struct task_struct *task;
+ const char *sp;
+ int len;
+
+ if (!try_module_get(THIS_MODULE))
+ return NULL;
+
+ /* The thread description gets truncated to 15 chars, can't be helped!
+ * Use 'ss7maint osstatus -t' to get the full description. */
+
+ /* Remove any leading space and truncate after second word */
+ if (desc[0] == ' ')
+ desc++;
+ len = 100;
+ sp = ss7strchr(desc, ' ');
+ if (sp != NULL) {
+ sp = ss7strchr(sp + 1, ' ');
+ if (sp != NULL)
+ len = sp - desc;
+ }
+
+ task = kthread_run(ss7_glue_thread_fn, thrp, "ss7:%.*s", len, desc);
+ if (IS_ERR(task)) {
+ module_put(THIS_MODULE);
+ return NULL;
+ }
+ return task;
+}
+
+void
+ss7_ms_delay(const unsigned int ms)
+{
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((unsigned long long)HZ * ms / 1000);
+}
+
+int
+ss7_os_get_ticks(void)
+{
+ return jiffies;
+}
+
+int
+ss7_os_ticks_to_us(int interval)
+{
+ return interval * 1000000 / HZ;
+}
+
+int
+ss7_os_ticks_to_ms(int interval)
+{
+ return interval * 1000 / HZ;
+}
+
+int
+ss7_os_ticks_to_secs(int interval)
+{
+ return interval / HZ;
+}
+
+unsigned int
+ss7_get_ms_time(void)
+{
+ static unsigned long epoch;
+ struct timespec now;
+
+ getrawmonotonic(&now);
+
+ if (epoch == 0)
+ epoch = now.tv_sec;
+
+ return (now.tv_sec - epoch) * 1000 + now.tv_nsec / 1000000;
+}
+
+struct acu_ss7maint_time {
+ unsigned int st_sec;
+ unsigned int st_usec;
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
+static inline void do_gettimeofday(struct timeval *tv)
+{
+ struct timespec64 ts;
+
+ ktime_get_real_ts64(&ts);
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec/1000u;
+}
+#endif
+
+void
+ss7_get_timestamp(struct acu_ss7maint_time *ptime)
+{
+ struct timeval tv;
+
+ /* do_gettimeofday() returns 'wall clock time'.
+ * It can go backwards. */
+ do_gettimeofday(&tv);
+ ptime->st_sec = tv.tv_sec;
+ ptime->st_usec = tv.tv_usec;
+}
+
+unsigned int
+ss7_get_elapsed(const struct acu_ss7maint_time *epoch)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+
+ return tv.tv_sec - epoch->st_sec;
+}
+
+void
+ss7_os_log_error(const char *text)
+{
+ printk(KERN_EMERG "ss7server: %s", text);
+ if (memcmp(text, "Assertion fail", 14) == 0) {
+ dump_stack();
+ /* Although we return, the caller sleeps forever */
+ /* Remember the 'stuck' tasks */
+ asserted_tasks[asserted_task_count++ & 15] = current;
+ }
+}
+
+/*---------------------------------------------------------------------**
+** Miscellanous string and memory functions **
+**---------------------------------------------------------------------*/
+
+void
+ss7memzero(void *buf, size_t len)
+{
+ memset(buf, 0, len);
+}
+
+void
+ss7memcpy(void *dest, const void *src, size_t len)
+{
+ memcpy(dest, src, len);
+}
+
+void
+ss7_memmove(void *dest, const void *src, size_t len)
+{
+ memmove(dest, src, len);
+}
+
+int
+ss7memcmp(const void *s1, const void *s2, size_t len)
+{
+ return memcmp(s1, s2, len);
+}
+
+unsigned int
+ss7strlen(const char *str)
+{
+ return strlen(str);
+}
+
+void
+ss7strcpy(char *dest, const char *src)
+{
+ strcpy(dest, src);
+}
+
+int
+ss7strcmp(const char *dest, const char *src)
+{
+ return strcmp(dest, src);
+}
+
+char *
+ss7strncpy(char *const s1, const char *s2, size_t n)
+{
+ return strncpy(s1, s2, n);
+}
+
+char *
+ss7strchr(const char *s, const int c)
+{
+ return strchr(s, c);
+}
+
+/*---------------------------------------------------------------------**
+** TCP/IP functions **
+**---------------------------------------------------------------------*/
+
+int
+ss7_sctp_supported(void)
+{
+ return 1;
+}
+
+unsigned int
+ss7_get_default_af_opts(unsigned int protocol, unsigned int port)
+{
+ /* The SS7 driver needs to know the which address families (IPv4 or IPv6)
+ * to use for listening sockets.
+ *
+ * Whether an IPV6 socket can accept IPV4 connections depends on
+ * the IPV6_V6ONLY socket option. The default for which depends
+ * on net.ipv6.bindv6only (which usually defaults to 0 - allowing IPV4).
+ * There also might be kernels where clearing IPV6_V6ONLY is disallowed.
+ *
+ * Normally only a single socket is created for each port since an IPv6
+ * socket can receive IPv4 connections. However a separate IPv4 socket
+ * can be requested.
+ *
+ * This function should return one of:
+ * SS7_AF_OPT_IPv6
+ * IPV6 socket with the default IPV6_V6ONLY value.
+ * SS7_AF_OPT_IPv6_V6ONLY_CLR
+ * IPV6 socket with IPV6_V6ONLY explicitly cleared.
+ * SS7_AF_OPT_IPv6_V6ONLY_SET
+ * IPV6 socket with IPV6_V6ONLY explicitly set.
+ * Possibly logically ored with:
+ * SS7_AF_OPT_IPv4
+ * A separate IPv4 socket.
+ *
+ * For flexibility the decision can be based on the protocol (either
+ * IPPROTO_SCTP or IPPROTO_TCP) or the port number.
+ *
+ * Default to creating a single socket and disabling IPV6_V6ONLY.
+ */
+#ifndef SS7_DEFAULT_AF_OPTS
+#define SS7_DEFAULT_AF_OPTS SS7_AF_OPT_IPv6
+#endif
+ return SS7_DEFAULT_AF_OPTS;
+}
+
+/* kernel_get/set_sockopt() prototypes have (char *) for the buffer.
+ * #define a (void *) cast.
+ */
+#define kernel_setsockopt(sock, level, name, val, len) \
+ kernel_setsockopt(sock, level, name, (void *)val, len)
+#define kernel_getsockopt(sock, level, name, val, len) \
+ kernel_getsockopt(sock, level, name, (void *)val, len)
+
+/* Note that we can't (easily) hold reference counts on the namespace
+ * because put_net() is GPL_ONLY.
+ * Instead we keep our own table and create a socket to hold the
+ * reference for us.
+ * Table entries 0 and 1 always refer to init_net and the namespace
+ * of the (last started) ss7 daemon. Neither is reference counted
+ * (although we hold a single reference on the latter).
+ * Higher entries are saved from invocations of 'ss7maint start'
+ * and 'firmware download'. */
+
+static struct ss7_ns_info {
+ struct net *ni_net_ns;
+ struct socket *ni_sock;
+ unsigned int ni_refcount;
+} ss7_ns_table[256];
+
+static struct socket *
+ss7_glue_create_ns_socket(struct net *net)
+{
+ struct socket *sock;
+
+ if (__sock_create(net, AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock, 0))
+ return NULL;
+ return sock;
+}
+
+void
+ss7_net_ns_get(unsigned int namespace)
+{
+ unsigned int idx = SS7_NET_NS_IDX(namespace);
+
+ if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
+ /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
+ return;
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ ss7_ns_table[idx].ni_refcount++;
+ ss7_mutex_exit(&ss7_glue_mutex);
+
+ ss7_trace_printf(0, "ss7_net_ns_get(%x): refcount %d, sock %p, net %p\n",
+ namespace, ss7_ns_table[idx].ni_refcount, ss7_ns_table[idx].ni_sock,
+ ss7_ns_table[idx].ni_net_ns);
+}
+
+void
+ss7_net_ns_put(unsigned int namespace)
+{
+ struct ss7_ns_info *ni;
+ unsigned int idx = SS7_NET_NS_IDX(namespace);
+
+ if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
+ /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
+ return;
+ ni = ss7_ns_table + idx;
+
+ ss7_trace_printf(0, "ss7_net_ns_put(%x): refcount %d, sock %p, net %p\n",
+ namespace, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ if (ni->ni_refcount && !--ni->ni_refcount) {
+ /* Last reference gone */
+ sock_release(ni->ni_sock);
+ ni->ni_net_ns = NULL;
+ ni->ni_sock = NULL;
+ }
+ ss7_mutex_exit(&ss7_glue_mutex);
+}
+
+static void
+ss7_net_ns_unload(void)
+{
+ unsigned int idx;
+ struct ss7_ns_info *ni;
+
+ for (idx = 1; idx < ARRAY_SIZE(ss7_ns_table); idx++) {
+ ni = ss7_ns_table + idx;
+ if (!ni->ni_sock)
+ continue;
+
+ /* This should only report anything for the 'daemon' slot */
+ printk(KERN_INFO "ss7_net_ns_unload(): idx %d, refcount %d, sock %p, net %p\n",
+ idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
+ sock_release(ni->ni_sock);
+ ni->ni_net_ns = NULL;
+ ni->ni_sock = NULL;
+ ni->ni_refcount = 0;
+ }
+}
+
+unsigned int
+ss7_net_ns_set(unsigned int new_namespace, unsigned int old_namespace)
+{
+ static unsigned int num_used_idx = 2;
+ unsigned int idx, free_idx;
+ struct ss7_ns_info *ni;
+ struct net *net;
+
+ /* The new_namespace should have the low 16 bits zero.
+ * The low bits of old_namespace indicate what was actually being used. */
+
+ if (new_namespace != SS7_NET_NS_START) {
+ ss7_net_ns_put(old_namespace);
+ return new_namespace == SS7_NET_NS_DAEMON ? SS7_NET_NS_DAEMON : SS7_NET_NS_INIT;
+ }
+
+ /* SS7_NET_NS_START - look for an entry for the namespace of the current
+ * process (which will be 'ss7maint start'). */
+ net = current->nsproxy->net_ns;
+
+ idx = SS7_NET_NS_IDX(old_namespace);
+ ni = ss7_ns_table + idx;
+ if (ni->ni_net_ns == net)
+ /* Unchanged index, no need to change reference count */
+ return SS7_NET_NS_START | idx;
+
+ /* Different slot needed, drop old reference */
+ ss7_net_ns_put(old_namespace);
+
+ /* Check init and daemon entries, neither goes away */
+ if (idx != SS7_NET_NS_IDX(SS7_NET_NS_INIT)
+ && net == &init_net)
+ return SS7_NET_NS_START | SS7_NET_NS_IDX(SS7_NET_NS_INIT);
+
+ idx = SS7_NET_NS_IDX(SS7_NET_NS_DAEMON);
+ ni = ss7_ns_table + idx;
+ if (net == ni->ni_net_ns)
+ return SS7_NET_NS_START | idx;
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+
+ /* Scan table for an existing reference */
+ free_idx = 0;
+ for (idx = 2; idx < num_used_idx; idx++) {
+ ni = ss7_ns_table + idx;
+ if (ni->ni_net_ns == net) {
+ /* found a match */
+ ni->ni_refcount++;
+ ss7_mutex_exit(&ss7_glue_mutex);
+ ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): found idx %d, refcount %d, sock %p, net %p\n",
+ new_namespace, old_namespace, idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
+ return SS7_NET_NS_START | idx;
+ }
+ if (!free_idx && !ni->ni_net_ns)
+ free_idx = idx;
+ }
+
+ /* Not found allocate lowest free slot */
+ if (!free_idx) {
+ if (num_used_idx >= ARRAY_SIZE(ss7_ns_table))
+ /* Table full, borked */
+ goto no_ref;
+ free_idx = num_used_idx++;
+ }
+
+ ni = &ss7_ns_table[free_idx];
+ ni->ni_sock = ss7_glue_create_ns_socket(net);
+ if (!ni->ni_sock)
+ goto no_ref;
+ ni->ni_net_ns = net;
+
+ ss7_mutex_exit(&ss7_glue_mutex);
+ ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): new idx %d, sock %p, net %p\n",
+ new_namespace, old_namespace, free_idx, ni->ni_sock, ni->ni_net_ns);
+
+ return SS7_NET_NS_START | free_idx;
+
+ no_ref:
+ ss7_mutex_exit(&ss7_glue_mutex);
+ ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): no_ref\n",
+ new_namespace, old_namespace);
+ return SS7_NET_NS_START;
+}
+
+void
+ss7_glue_daemon_open(void)
+{
+ struct ss7_ns_info *ni = &ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_DAEMON)];
+ struct net *net = current->nsproxy->net_ns;
+
+ /* Save (and reference count) the network namespace the ss7 daemon
+ * is started in. */
+
+ /* Initialise the entry for init_net here - has to be done somewhere. */
+ ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_INIT)].ni_net_ns = &init_net;
+
+ if (net == ni->ni_net_ns)
+ /* Unchanged */
+ return;
+
+ if (ni->ni_sock)
+ sock_release(ni->ni_sock);
+ ni->ni_sock = NULL;
+
+ if (net != &init_net && !((ni->ni_sock = ss7_glue_create_ns_socket(net))))
+ /* Can't create socket, default to global namespace */
+ net = &init_net;
+
+ ni->ni_net_ns = net;
+}
+
+int
+ss7_socket(int family, int type, int protocol, unsigned int namespace, struct socket **sockp)
+{
+ struct socket *sock;
+ struct net *net;
+ unsigned int one = 1U;
+ int rval;
+
+ net = ss7_ns_table[SS7_NET_NS_IDX(namespace)].ni_net_ns;
+ if (!net)
+ net = &init_net;
+
+ /* If we have to autoload the sctp module, we might re-enter it
+ * before it has finished initialising - might go 'boom'. */
+ ss7_mutex_enter(&ss7_glue_mutex);
+
+ /* sock_create_kern() creates a socket that doesn't hold a reference
+ * to the namespace (they get used for sockets needed by the protocol
+ * stack code itself).
+ * We need a socket that holds a reference to the namespace, so create
+ * a 'user' socket in a specific namespace.
+ * This adds an extra security check which we should pass because all the
+ * sockets are created by kernel threads.
+ */
+ rval = __sock_create(net, family, type, protocol, sockp, 0);
+ ss7_mutex_exit(&ss7_glue_mutex);
+ if (rval != 0)
+ return rval;
+ sock = *sockp;
+
+ kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
+
+ return 0;
+}
+
+void
+ss7_setsockopt_nodelay(struct socket *sock, int enabled)
+{
+ kernel_setsockopt(sock, SK_PROTOCOL(sock),
+ SK_PROTOCOL(sock) == IPPROTO_TCP ? TCP_NODELAY : SCTP_NODELAY,
+ &enabled, sizeof enabled);
+}
+
+static void
+ss7_sctp_set_opts(struct socket *sock)
+{
+ struct sctp_event_subscribe events;
+ int len, rval;
+
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return;
+
+ len = sizeof events;
+ rval = kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, &len);
+ if (rval != 0)
+ return;
+
+ /* We need to know the stream and ppid */
+ events.sctp_data_io_event = 1;
+ /* Enable notifications to detect connection restart */
+ events.sctp_association_event = 1;
+ kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof events);
+}
+
+unsigned int
+ss7_get_max_sctp_ostreams(struct socket *sock)
+{
+ struct sctp_status sstat;
+ int len;
+
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return 0;
+
+ len = sizeof sstat;
+ if (kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &sstat, &len))
+ return 0;
+
+ return sstat.sstat_outstrms;
+}
+
+void
+ss7_set_max_sctp_streams(struct socket *sock, unsigned int max_streams)
+{
+ struct sctp_initmsg sinit;
+
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return;
+
+ memset(&sinit, 0, sizeof sinit);
+
+ sinit.sinit_num_ostreams = max_streams;
+ sinit.sinit_max_instreams = max_streams;
+ kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_INITMSG, &sinit, sizeof sinit);
+}
+
+void
+ss7_trans_setsockopt(struct socket *sock)
+{
+ unsigned int one = 1U;
+
+ ss7_setsockopt_nodelay(sock, 1);
+ ss7_sctp_set_opts(sock);
+ if (SK_PROTOCOL(sock) == IPPROTO_TCP)
+ kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
+}
+
+void
+ss7_transbind_setsockopt(struct socket *sock)
+{
+ /* Set options for a listening socket */
+ ss7_sctp_set_opts(sock);
+
+ /* M3UA may need 16 data streams, it is just TFH to configure this */
+ ss7_set_max_sctp_streams(sock, 1 + 16);
+}
+
+#define IP_ADDR_LEN(sa) ((sa)->sin6_family == AF_INET6 ? sizeof *(sa) : 16)
+int
+ss7_connect(struct socket *sock, struct sockaddr_in6 *sa)
+{
+ return kernel_connect(sock, (void *)sa, IP_ADDR_LEN(sa), O_RDWR);
+}
+
+int
+ss7_bind(struct socket *sock, struct sockaddr_in6 *sa, unsigned int af_opts)
+{
+ /* If we are binding INADDR6_ANY to an IPv6 socket (typically for
+ * a listening socket) then we probably want to ensure that IPV6_V6ONLY
+ * is 0 so that the socket will also be given IPv4 connections. */
+ if (sa->sin6_family == AF_INET6 && af_opts & SS7_AF_OPT_IPv6_V6ONLY
+ && sa->sin6_addr.in6_u.u6_addr32[0] == 0
+ && (sa->sin6_addr.in6_u.u6_addr32[1]
+ | sa->sin6_addr.in6_u.u6_addr32[2]
+ | sa->sin6_addr.in6_u.u6_addr32[3]) == 0) {
+ int v6only = af_opts & 1;
+ kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof v6only);
+ }
+
+ return kernel_bind(sock, (void *)sa, IP_ADDR_LEN(sa));
+}
+
+int
+ss7_bindx(struct socket *sock, struct sockaddr_in6 *sa)
+{
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return -EPROTONOSUPPORT;
+
+ return kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_SOCKOPT_BINDX_ADD,
+ sa, IP_ADDR_LEN(sa));
+}
+
+int
+ss7_listen(struct socket *sock, int len)
+{
+ return kernel_listen(sock, len);
+}
+
+int
+ss7_accept(struct socket *sock, struct socket **new_sockp, int flags)
+{
+ return kernel_accept(sock, new_sockp, flags);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
+static inline int
+ss7_kernel_getsockname(struct socket *sock, struct sockaddr *address)
+{
+ int err, len;
+
+ err = kernel_getsockname(sock, (struct sockaddr *)address, &len);
+ return err ? err : len;
+}
+#define kernel_getsockname ss7_kernel_getsockname
+
+static inline int
+ss7_kernel_getpeername(struct socket *sock, struct sockaddr *address)
+{
+ int err, len;
+
+ err = kernel_getpeername(sock, (struct sockaddr *)address, &len);
+ return err ? err : len;
+}
+#define kernel_getpeername ss7_kernel_getpeername
+#endif
+
+int
+ss7_get_loc_port(struct socket *sock)
+{
+ char address[128 /*MAX_SOCK_ADDR*/];
+ int len;
+
+ len = kernel_getsockname(sock, (struct sockaddr *)address);
+ if (len < 0)
+ return 0;
+
+ /* This works well enough for IPv4 and IPv6 */
+ return ntohs(((struct sockaddr_in *)address)->sin_port);
+}
+
+int
+ss7_get_rem_addr(struct socket *sock, struct sockaddr_in6 *saddr)
+{
+ int len;
+
+ len = kernel_getpeername(sock, (struct sockaddr *)saddr);
+ if (len < 0)
+ return len;
+
+ if (len > sizeof *saddr)
+ printk(KERN_EMERG "ss7server: socket address (family %d) %d > %d",
+ saddr->sin6_family, len, (int)sizeof *saddr);
+
+ return 0;
+}
+
+int
+ss7_shutdown(struct socket *sock, int how)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
+ if (SK_PROTOCOL(sock) == IPPROTO_SCTP) {
+ struct linger linger;
+
+ /* If we call kernel_sock_shutdown() then the connection isn't released
+ * until all outstanding data has been acked.
+ * If the remote system sends an INIT (restarting the connection)
+ * while the linux kernel is waiting for data to be acked then it
+ * will never disconnect.
+ * Enabling 'linger' with a delay of zero causes sock_release()
+ * to abort the connection (sends an ABORT chunk).
+ *
+ * The ss7 code never needs to wait for sent data to be acked,
+ * so aborting the connection doesn't really matter.
+ * All calls to ss7_shutdown() are immediately followed by calls to
+ * ss7_closesocket().
+ *
+ * Plausibly we should always abort connections if we are disconnecting
+ * due to an application level timeout.
+ *
+ * Fixed by the kernel patch:
+ * "sctp: handle association restarts when the socket is closed"
+ * Known to be included in the following kernels:
+ * - mainline 3.18
+ * - Ubuntu 3.13.11.11
+ * Queued for 3.10-stable, 3.14-stable, 3.16-stable and 3.17-stable
+ */
+
+ linger.l_onoff = 1;
+ linger.l_linger = 0;
+ kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof linger);
+
+ return 0;
+ }
+#endif
+ return kernel_sock_shutdown(sock, how);
+}
+
+void
+ss7_closesocket(struct socket *sock)
+{
+ sock_release(sock);
+}
+
+int
+ss7_send(struct socket *sock, struct ss7_iovec *iov, int iovlen, int totlen,
+ void *ctl, int ctl_len, unsigned int flags)
+{
+ struct msghdr msg;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_control = ctl;
+ msg.msg_controllen = ctl_len;
+ msg.msg_flags = flags | MSG_NOSIGNAL;
+
+ return kernel_sendmsg(sock, &msg, iov, iovlen, totlen);
+}
+
+int
+ss7_recv(struct socket *sock, unsigned char *data, int length, int flags)
+{
+ struct kvec iov;
+ struct msghdr msg;
+
+ if (!sock->sk)
+ return 0;
+
+ iov.iov_len = length;
+ iov.iov_base = data;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ return kernel_recvmsg(sock, &msg, &iov, 1, length, 0);
+}
+
+int
+ss7_recv_sctp(struct socket *sock, void *buf_1, int len_1, void *buf_2,
+ int len_2, struct ss7_msgb *ss7_msg)
+{
+ struct msghdr msg;
+ struct kvec iov[2];
+ unsigned char *data = buf_1;
+ int msg_len, ctl_len;
+ int rval;
+ union {
+ struct cmsghdr cmsg;
+ unsigned int buf[16];
+ } ctlbuf;
+
+ if (!sock->sk)
+ return 0;
+
+ /* For SCTP each recvmsg should give us a single data record.
+ * Since we only ever send SIGTRAN encoded messages bytes 4-7 are the
+ * length - and should match that of the sctp data chunk.
+ * buf_1/len_1 refer to the normal ss7 message buffer area, buf_2/len_2
+ * are per-socket. Long messages get copied together by the caller.
+ * The result is always a single valid SIGTRAN message */
+
+ iov[0].iov_base = buf_1;
+ iov[0].iov_len = len_1;
+ iov[1].iov_base = buf_2;
+ iov[1].iov_len = len_2;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_control = &ctlbuf;
+ msg.msg_controllen = sizeof ctlbuf;
+ msg.msg_flags = 0;
+
+ rval = kernel_recvmsg(sock, &msg, iov, 2, len_1 + len_2, 0);
+
+ if (rval <= 0)
+ /* Don't return EBADMSG here */
+ return rval != -EBADMSG ? rval : -EIO;
+
+ if (msg.msg_flags & MSG_NOTIFICATION)
+ /* msg data is a notification */
+ return -EBADMSG;
+
+ ctl_len = (char *)msg.msg_control - (char *)&ctlbuf;
+ if (ctl_len >= ctlbuf.cmsg.cmsg_len
+ && ctlbuf.cmsg.cmsg_level == IPPROTO_SCTP
+ && ctlbuf.cmsg.cmsg_type == SCTP_SNDRCV) {
+ struct sctp_sndrcvinfo *sinfo = CMSG_DATA(&ctlbuf.cmsg);
+ ss7_trans_set_msg_info(ss7_msg, sinfo->sinfo_stream, sinfo->sinfo_ppid);
+ }
+
+ msg_len = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
+ if (msg_len >= 65556)
+ /* Disbelieve this is valid data */
+ return -EIO;
+
+ if (rval != msg_len || !(msg.msg_flags & MSG_EOR))
+ return -EIO;
+ return rval;
+}
+
+int
+ss7_trans_init_sctp_sinfo(void *buf, int maxlen, __u16 **stream, __u32 **ppid)
+{
+ struct cmsghdr *cmsg;
+ struct sctp_sndrcvinfo *sinfo;
+
+ if (maxlen < CMSG_LEN(sizeof *sinfo))
+ return -1;
+
+ cmsg = buf;
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof *sinfo);
+ sinfo = CMSG_DATA(cmsg);
+ memset(sinfo, 0, sizeof *sinfo);
+ *stream = &sinfo->sinfo_stream;
+ *ppid = &sinfo->sinfo_ppid;
+
+ return CMSG_LEN(sizeof *sinfo);
+}
\ No newline at end of file
diff --git a/a/2.hdr b/a/2.hdr
deleted file mode 100644
index 7ca41cd..0000000
--- a/a/2.hdr
+++ /dev/null
@@ -1,6 +0,0 @@
-Content-Type: text/plain; name=ss7osglue.c; charset=WINDOWS-1252
-Content-Description: ss7osglue.c
-Content-Disposition: attachment; filename="ss7osglue.c"; size=36047;
- creation-date="Thu, 26 Sep 2019 10:15:35 GMT";
- modification-date="Thu, 26 Sep 2019 10:15:35 GMT"
-Content-Transfer-Encoding: base64
diff --git a/a/2.txt b/a/2.txt
deleted file mode 100644
index c3e7b95..0000000
--- a/a/2.txt
+++ /dev/null
@@ -1,1381 +0,0 @@
-#ident "@(#) (c) Aculab plc $Header: /home/cvs/repository/ss7/stack/src/driver/linux/ss7osglue.c,v 1.157 2019-08-29 16:09:14 davidla Exp $ $Name: $"
-#ifndef MODULE
-#define MODULE
-#endif
-
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-#error minimum kernel version is 2.6.28
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
-#include <generated/autoconf.h>
-#else
-#include <linux/autoconf.h>
-#endif
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/kmod.h>
-#include <linux/string.h>
-#include <linux/sched.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
-#include <linux/sched/signal.h>
-#endif
-#include <linux/wait.h>
-#include <linux/socket.h>
-#include <linux/signal.h>
-#include <linux/poll.h>
-#include <linux/net.h>
-#include <linux/nsproxy.h>
-#include <linux/in.h>
-#include <linux/reboot.h>
-#include <asm/atomic.h>
-#include <asm/uaccess.h>
-
-#include <linux/kthread.h>
-
-/* This is only in the kernel build tree */
-#include <net/sock.h>
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
-#include <uapi/linux/sctp.h>
-#else
-#include <net/sctp/user.h> /* netinet/sctp.h ought to be this file */
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
-#define wait_queue_head __wait_queue_head
-#define wait_queue_entry __wait_queue
-#endif
-
-#define SK_PROTOCOL(sock) (sock)->sk->sk_protocol
-
-extern void ss7_trace_mem(int, void *, int, const char *, ...);
-extern void ss7_trace_printf(int, const char *, ...);
-
-/* Aculab DACP interfaces - these are in aculab's kern_if.h */
-void *dacp_symbol_get(const char *);
-int dacp_symbol_release(const char *);
-
-MODULE_AUTHOR("Aculab");
-MODULE_LICENSE("Proprietary");
-
-#include "ss7osglue.h"
-
-/* Mutex for driver interface code */
-static struct mutex ss7_glue_mutex;
-
-static int ss7dev_major;
-static const void *ss7_dtls_handle;
-static int ss7_use_count;
-static int ss7_stop_pid;
-
-static struct task_struct *asserted_tasks[16];
-static unsigned int asserted_task_count;
-
-typedef char ss7_verify_const[ SS7_SOCK_STREAM == SOCK_STREAM && SS7_SOCK_SEQPACKET == SOCK_SEQPACKET ? 1 : -1];
-
-static void ss7_net_ns_unload(void);
-
-#define TCP_NODELAY 1
-
-static int ss7_glue_open(struct inode *, struct file *);
-static int ss7_glue_release(struct inode *, struct file *);
-static long ss7_glue_unlocked_ioctl(struct file *, unsigned int, unsigned long);
-static unsigned int ss7_glue_poll(struct file *const, poll_table *);
-
-static struct file_operations ss7dev_fop =
-{
- open: ss7_glue_open,
- release: ss7_glue_release,
- unlocked_ioctl: ss7_glue_unlocked_ioctl,
- compat_ioctl: ss7_glue_unlocked_ioctl,
- poll: ss7_glue_poll,
- owner: THIS_MODULE
-};
-
-static int ss7_reboot_notify(struct notifier_block *nb, unsigned long action,
- void *data)
-{
- /* System being rebooted.
- * I added this hoping to use it to get the ss7maint daemon to exit,
- * but it isn't called until all user processes have died.
- * Leave it here - might be useful one day. */
- return 0;
-}
-
-static struct notifier_block ss7_reboot_notifier_block = {
- .notifier_call = ss7_reboot_notify,
-};
-
-static int
-ss7_init_fail(int rval)
-{
- if (ss7dev_major > 0)
- unregister_chrdev(ss7dev_major, "ss7server");
- return rval;
-}
-
-static int
-ss7_init_mod(void)
-{
- const void *(*dtls_register)(const char *, int (*)(struct dtls_get_if *));
- int rval;
-
- ss7_mutex_init(&ss7_glue_mutex);
-
- printk(KERN_INFO "%s\n", ss7version);
-
- ss7dev_major = register_chrdev(0, "ss7server", &ss7dev_fop);
-
- if (ss7dev_major < 0) {
- printk(KERN_INFO "ss7server: register_chrdev() failed: %d\n",
- ss7dev_major);
- return ss7_init_fail(ss7dev_major);
- }
-
- rval = ss7_driver_init();
- if (rval != 0) {
- printk(KERN_INFO "ss7server: ss7_driver_init() failed: %d\n", rval);
- return ss7_init_fail(-EIO);
- }
-
- dtls_register = dacp_symbol_get("acuc_dtls_register");
- if (dtls_register == NULL)
- printk(KERN_INFO "ss7server: cannot locate \"acuc_dtls_register\"\n");
- else
- ss7_dtls_handle = dtls_register(DYNAMIC_TLS_PREFIX "ss7",
- ss7_tls_get_if);
-
- register_reboot_notifier(&ss7_reboot_notifier_block);
- return 0;
-}
-
-static void
-ss7_cleanup_mod(void)
-{
- int (*dtls_unregister)(const void *);
-
- unregister_reboot_notifier(&ss7_reboot_notifier_block);
-
- if (ss7_dtls_handle != NULL) {
- dtls_unregister = dacp_symbol_get("acuc_dtls_unregister");
- dacp_symbol_release("acuc_dtls_register");
- if (dtls_unregister != NULL) {
- dtls_unregister(ss7_dtls_handle);
- dacp_symbol_release("acuc_dtls_unregister");
- }
- }
-
- ss7_init_fail(0);
-
- printk(KERN_INFO "Aculab ss7server: driver unloaded\n");
-}
-
-module_init(ss7_init_mod)
-module_exit(ss7_cleanup_mod)
-
-static int
-ss7_glue_open(struct inode *const inode, struct file *const filp)
-{
- int rval, pid;
-
- if (filp->private_data)
- /* Duplicate open */
- return 0;
-
- ss7_mutex_enter(&ss7_glue_mutex);
- if (ss7_use_count < 0) {
- /* ss7_driver_shutdown() has been called, to late to do anything */
- ss7_mutex_exit(&ss7_glue_mutex);
- return -EIO;
- }
- ss7_use_count++;
- ss7_mutex_exit(&ss7_glue_mutex);
-
- rval = ss7_devif_open(&filp->private_data);
- if (rval != 0) {
- ss7_mutex_enter(&ss7_glue_mutex);
- ss7_use_count--;
- ss7_mutex_exit(&ss7_glue_mutex);
- pid = ss7_pid();
- if (pid != ss7_stop_pid)
- printk(KERN_INFO "ss7_devif_open() pid %d failed ss7 error %d\n",
- pid, rval);
- return -EIO;
- }
-
- return 0;
-}
-
-static int
-ss7_glue_release(struct inode *const inode, struct file *const filp)
-{
- if (filp->private_data)
- ss7_devif_close(filp->private_data);
-
- ss7_mutex_enter(&ss7_glue_mutex);
- ss7_use_count--;
-
- if (ss7_use_count == 0 && ss7_stop_pid != 0) {
- /* Last user process has gone, complete shutdown functions */
- ss7_net_ns_unload();
- /* Stop any more opens */
- ss7_use_count = -1;
- ss7_driver_shutdown();
- }
-
- ss7_mutex_exit(&ss7_glue_mutex);
-
- return 0;
-}
-
-static long
-ss7_glue_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- if (!filp->private_data)
- return -ENODEV;
-
- switch (cmd) {
-
- case SS7_STOP: /* ss7maint shutting us down */
- /* Start shutdown now, will complete on last close */
- ss7_driver_stop();
- ss7_stop_pid = ss7_pid();
- return 0;
-
- /* Request from ss7maint or user application */
- case SS7_USER_IOCTL_CODE:
- return ss7dev_ioctl(filp->private_data, cmd, arg);
-
- default:
- return -ENOTTY;
- }
-}
-
-static unsigned int
-ss7_glue_poll(struct file *filp, poll_table *pt)
-{
- poll_wait(filp, *ss7_devif_get_pollqueue_head(filp->private_data), pt);
- return ss7_devif_get_poll_status(filp->private_data);
-}
-
-void *
-ss7_os_malloc(int s, int ss7_flags)
-{
- return kmalloc(s, GFP_KERNEL);
-}
-
-void
-ss7_os_free(void *p)
-{
- kfree(p);
-}
-
-void
-ss7_poll_queue_head_deinit(wait_queue_head_t **pqhp)
-{
- ss7_os_free(*pqhp);
-}
-
-int
-ss7_poll_queue_head_init(wait_queue_head_t **pqhp)
-{
- wait_queue_head_t *pqh = ss7_os_malloc(sizeof *pqh, 0);
- if (pqh == NULL)
- return -1;
- init_waitqueue_head(pqh);
- *pqhp = pqh;
- return 0;
-}
-
-void
-ss7_pollwakeup(wait_queue_head_t **pqh, unsigned int poll_event)
-{
- wake_up(*pqh);
-}
-
-void
-ss7_kill_task(struct task_struct *task, int signo)
-{
- /* Send signal even though set to SIG_IGN */
- force_sig(signo, task);
-}
-
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
-/* spinlock_t is a typedef for an unnamed structure so we can't
- * make 'struct spinlock' match the kernel spinlock type. */
-#define SPINLOCK_CAST (spinlock_t *)
-#else
-#define SPINLOCK_CAST
-#endif
-
-size_t
-ss7_spin_lock_size(void)
-{
- return sizeof *SPINLOCK_CAST(struct spinlock *)0;
-}
-
-void
-ss7_spin_lock_init(struct spinlock *s)
-{
- spin_lock_init(SPINLOCK_CAST s);
-}
-
-void
-ss7_spin_lock_enter(struct spinlock *s)
-{
- spin_lock(SPINLOCK_CAST s);
-}
-
-void
-ss7_spin_lock_exit(struct spinlock *s)
-{
- spin_unlock(SPINLOCK_CAST s);
-}
-
-size_t
-ss7_mutex_size(void)
-{
- return sizeof(struct mutex);
-}
-
-void
-ss7_mutex_init(struct mutex *s)
-{
- mutex_init(s);
-}
-
-void
-ss7_mutex_enter(struct mutex *s)
-{
- mutex_lock(s);
-}
-
-int
-ss7_mutex_enter_tmo(struct mutex *s, int max_wait)
-{
- /* There is no mutex_enter_timeout() however this was all added
- * to stop status commands sleeping forever when a process has
- * 'oopsed' with a mutex held.
- * Do a sneak check on the state of any owning task then
- * wait interruptibly.
- * ^C should error out the status call. */
-
- /* If uncontended just acquire */
- if (mutex_trylock(s))
- return 1;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
- {
- struct task_struct *owner;
- int state;
-
- spin_lock(&s->wait_lock);
- owner = __mutex_owner(s);
- state = owner ? owner->state : 0;
- spin_unlock(&s->wait_lock);
- if (state & TASK_DEAD)
- /* mutex will never be released, treat as timeout */
- return 0;
- }
-#endif
-
- /* If C7_ASSERT() has been called, just let everyone in */
- if (asserted_task_count)
- return 0;
-
- return mutex_lock_interruptible(s) ? -1 /* EINTR */ : 1 /* acquired */;
-}
-
-void
-ss7_mutex_exit(struct mutex *s)
-{
- mutex_unlock(s);
-}
-
-size_t
-ss7_cv_size(void)
-{
- return sizeof(wait_queue_head_t);
-}
-
-void
-ss7_cv_init(wait_queue_head_t *const v)
-{
- init_waitqueue_head(v);
-}
-
-static int
-ss7_schedule_tmo(int tmo_ms)
-{
- int tmo_jiffies;
-
- /* Really sleep - unless woken since unlocking spinlock */
- if (tmo_ms >= 0) {
- if (tmo_ms <= 1)
- tmo_jiffies = tmo_ms;
- else
- /* Convert to jiffies and round up */
- tmo_jiffies = 1 + (tmo_ms + 1 - 1) * 16 / (16000/HZ);
- /* Return value of schedule_timeout() is unexpired timeout */
- /* We want 0 for 'timedout' (to match cv_wait_sig()) */
- return schedule_timeout(tmo_jiffies) != 0;
- }
-
- schedule();
- if (!signal_pending(current))
- /* Woken by the event */
- return 1;
-
- /* Report 0 for a signal, except -1 for SIGKILL (reboot) */
- return sigismember(¤t->pending.signal, SIGKILL) ? -1 : 0;
-}
-
-int
-ss7_cv_wait_guts(wait_queue_head_t *cvp, struct mutex *mtxp,
- int interruptible, int tmo_ms)
-{
- int r;
- struct wait_queue_entry w;
- int sleep_state;
-
- init_waitqueue_entry(&w, current);
-
- /* Tell scheduler we are going to sleep... */
- if (signal_pending(current) && !interruptible)
- /* We don't want waking immediately (again) */
- sleep_state = TASK_UNINTERRUPTIBLE;
- else
- sleep_state = TASK_INTERRUPTIBLE;
- set_current_state(sleep_state);
-
- /* Connect to condition variable ... */
- add_wait_queue(cvp, &w);
- mutex_unlock(mtxp); /* Release mutex */
-
- r = ss7_schedule_tmo(tmo_ms);
-
- /* Disconnect from condition variable ... */
- remove_wait_queue(cvp, &w);
-
- /* Re-acquire mutex */
- mutex_lock(mtxp);
-
- /* return 1 if woken, 0 if timed_out/signal, -1 if SIGKILL */
- return r;
-}
-
-int
-ss7_cv_wait_spin_lock(wait_queue_head_t *cvp, struct spinlock *lock,
- int interruptible, int tmo_ms)
-{
- int r;
- struct wait_queue_entry w;
- int sleep_state;
-
- init_waitqueue_entry(&w, current);
-
- /* Tell scheduler we are going to sleep... */
- if (signal_pending(current) && !interruptible)
- /* We don't want waking immediately (again) */
- sleep_state = TASK_UNINTERRUPTIBLE;
- else
- sleep_state = TASK_INTERRUPTIBLE;
- set_current_state(sleep_state);
-
- /* Connect to condition variable ... */
- add_wait_queue(cvp, &w);
- spin_unlock(SPINLOCK_CAST lock);
-
- r = ss7_schedule_tmo(tmo_ms);
-
- /* Disconnect from condition variable ... */
- remove_wait_queue(cvp, &w);
-
- /* Re-acquire mutex */
- spin_lock(SPINLOCK_CAST lock);
-
- return r;
-}
-
-/*---------------------------------------------------------------------**
-** ss7_cv_broadcast **
-** Awaken all threads that are sleeping on a condition variable. **
-** Caller must use the associated mutex sensibly, i.e. ... **
-** acquire the mutex **
-** Set some flag that a sleeping thread will check for **
-** ss7_cv_broadcast() **
-** release the mutex **
-**---------------------------------------------------------------------*/
-
-void
-ss7_cv_broadcast(wait_queue_head_t *const cvp)
-{
- wake_up(cvp);
-}
-
-
-unsigned long
-ss7_copy_to_user(void *to, const void *from, unsigned long c)
-{
- return copy_to_user(to, from, c);
-}
-
-unsigned long
-ss7_copy_from_user(void *to, const void *from, unsigned long c)
-{
- return copy_from_user(to, from, c);
-}
-
-unsigned int
-ss7_pid(void)
-{
- return current->pid;
-}
-
-struct task_struct *
-ss7_current_task(void)
-{
- return current;
-}
-
-unsigned int
-ss7_task_pid(struct task_struct *task)
-{
- return task->pid;
-}
-
-int
-ss7_glue_thread_fn(void *ss7_thread)
-{
- ss7_thread_run(ss7_thread);
- module_put_and_exit(0);
- return 0;
-}
-
-struct task_struct *
-ss7_os_thread_create(struct ss7_thread *thrp, const char *desc)
-{
- struct task_struct *task;
- const char *sp;
- int len;
-
- if (!try_module_get(THIS_MODULE))
- return NULL;
-
- /* The thread description gets truncated to 15 chars, can't be helped!
- * Use 'ss7maint osstatus -t' to get the full description. */
-
- /* Remove any leading space and truncate after second word */
- if (desc[0] == ' ')
- desc++;
- len = 100;
- sp = ss7strchr(desc, ' ');
- if (sp != NULL) {
- sp = ss7strchr(sp + 1, ' ');
- if (sp != NULL)
- len = sp - desc;
- }
-
- task = kthread_run(ss7_glue_thread_fn, thrp, "ss7:%.*s", len, desc);
- if (IS_ERR(task)) {
- module_put(THIS_MODULE);
- return NULL;
- }
- return task;
-}
-
-void
-ss7_ms_delay(const unsigned int ms)
-{
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((unsigned long long)HZ * ms / 1000);
-}
-
-int
-ss7_os_get_ticks(void)
-{
- return jiffies;
-}
-
-int
-ss7_os_ticks_to_us(int interval)
-{
- return interval * 1000000 / HZ;
-}
-
-int
-ss7_os_ticks_to_ms(int interval)
-{
- return interval * 1000 / HZ;
-}
-
-int
-ss7_os_ticks_to_secs(int interval)
-{
- return interval / HZ;
-}
-
-unsigned int
-ss7_get_ms_time(void)
-{
- static unsigned long epoch;
- struct timespec now;
-
- getrawmonotonic(&now);
-
- if (epoch == 0)
- epoch = now.tv_sec;
-
- return (now.tv_sec - epoch) * 1000 + now.tv_nsec / 1000000;
-}
-
-struct acu_ss7maint_time {
- unsigned int st_sec;
- unsigned int st_usec;
-};
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
-static inline void do_gettimeofday(struct timeval *tv)
-{
- struct timespec64 ts;
-
- ktime_get_real_ts64(&ts);
- tv->tv_sec = ts.tv_sec;
- tv->tv_usec = ts.tv_nsec/1000u;
-}
-#endif
-
-void
-ss7_get_timestamp(struct acu_ss7maint_time *ptime)
-{
- struct timeval tv;
-
- /* do_gettimeofday() returns 'wall clock time'.
- * It can go backwards. */
- do_gettimeofday(&tv);
- ptime->st_sec = tv.tv_sec;
- ptime->st_usec = tv.tv_usec;
-}
-
-unsigned int
-ss7_get_elapsed(const struct acu_ss7maint_time *epoch)
-{
- struct timeval tv;
- do_gettimeofday(&tv);
-
- return tv.tv_sec - epoch->st_sec;
-}
-
-void
-ss7_os_log_error(const char *text)
-{
- printk(KERN_EMERG "ss7server: %s", text);
- if (memcmp(text, "Assertion fail", 14) == 0) {
- dump_stack();
- /* Although we return, the caller sleeps forever */
- /* Remember the 'stuck' tasks */
- asserted_tasks[asserted_task_count++ & 15] = current;
- }
-}
-
-/*---------------------------------------------------------------------**
-** Miscellanous string and memory functions **
-**---------------------------------------------------------------------*/
-
-void
-ss7memzero(void *buf, size_t len)
-{
- memset(buf, 0, len);
-}
-
-void
-ss7memcpy(void *dest, const void *src, size_t len)
-{
- memcpy(dest, src, len);
-}
-
-void
-ss7_memmove(void *dest, const void *src, size_t len)
-{
- memmove(dest, src, len);
-}
-
-int
-ss7memcmp(const void *s1, const void *s2, size_t len)
-{
- return memcmp(s1, s2, len);
-}
-
-unsigned int
-ss7strlen(const char *str)
-{
- return strlen(str);
-}
-
-void
-ss7strcpy(char *dest, const char *src)
-{
- strcpy(dest, src);
-}
-
-int
-ss7strcmp(const char *dest, const char *src)
-{
- return strcmp(dest, src);
-}
-
-char *
-ss7strncpy(char *const s1, const char *s2, size_t n)
-{
- return strncpy(s1, s2, n);
-}
-
-char *
-ss7strchr(const char *s, const int c)
-{
- return strchr(s, c);
-}
-
-/*---------------------------------------------------------------------**
-** TCP/IP functions **
-**---------------------------------------------------------------------*/
-
-int
-ss7_sctp_supported(void)
-{
- return 1;
-}
-
-unsigned int
-ss7_get_default_af_opts(unsigned int protocol, unsigned int port)
-{
- /* The SS7 driver needs to know the which address families (IPv4 or IPv6)
- * to use for listening sockets.
- *
- * Whether an IPV6 socket can accept IPV4 connections depends on
- * the IPV6_V6ONLY socket option. The default for which depends
- * on net.ipv6.bindv6only (which usually defaults to 0 - allowing IPV4).
- * There also might be kernels where clearing IPV6_V6ONLY is disallowed.
- *
- * Normally only a single socket is created for each port since an IPv6
- * socket can receive IPv4 connections. However a separate IPv4 socket
- * can be requested.
- *
- * This function should return one of:
- * SS7_AF_OPT_IPv6
- * IPV6 socket with the default IPV6_V6ONLY value.
- * SS7_AF_OPT_IPv6_V6ONLY_CLR
- * IPV6 socket with IPV6_V6ONLY explicitly cleared.
- * SS7_AF_OPT_IPv6_V6ONLY_SET
- * IPV6 socket with IPV6_V6ONLY explicitly set.
- * Possibly logically ored with:
- * SS7_AF_OPT_IPv4
- * A separate IPv4 socket.
- *
- * For flexibility the decision can be based on the protocol (either
- * IPPROTO_SCTP or IPPROTO_TCP) or the port number.
- *
- * Default to creating a single socket and disabling IPV6_V6ONLY.
- */
-#ifndef SS7_DEFAULT_AF_OPTS
-#define SS7_DEFAULT_AF_OPTS SS7_AF_OPT_IPv6
-#endif
- return SS7_DEFAULT_AF_OPTS;
-}
-
-/* kernel_get/set_sockopt() prototypes have (char *) for the buffer.
- * #define a (void *) cast.
- */
-#define kernel_setsockopt(sock, level, name, val, len) \
- kernel_setsockopt(sock, level, name, (void *)val, len)
-#define kernel_getsockopt(sock, level, name, val, len) \
- kernel_getsockopt(sock, level, name, (void *)val, len)
-
-/* Note that we can't (easily) hold reference counts on the namespace
- * because put_net() is GPL_ONLY.
- * Instead we keep our own table and create a socket to hold the
- * reference for us.
- * Table entries 0 and 1 always refer to init_net and the namespace
- * of the (last started) ss7 daemon. Neither is reference counted
- * (although we hold a single reference on the latter).
- * Higher entries are saved from invocations of 'ss7maint start'
- * and 'firmware download'. */
-
-static struct ss7_ns_info {
- struct net *ni_net_ns;
- struct socket *ni_sock;
- unsigned int ni_refcount;
-} ss7_ns_table[256];
-
-static struct socket *
-ss7_glue_create_ns_socket(struct net *net)
-{
- struct socket *sock;
-
- if (__sock_create(net, AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock, 0))
- return NULL;
- return sock;
-}
-
-void
-ss7_net_ns_get(unsigned int namespace)
-{
- unsigned int idx = SS7_NET_NS_IDX(namespace);
-
- if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
- /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
- return;
-
- ss7_mutex_enter(&ss7_glue_mutex);
- ss7_ns_table[idx].ni_refcount++;
- ss7_mutex_exit(&ss7_glue_mutex);
-
- ss7_trace_printf(0, "ss7_net_ns_get(%x): refcount %d, sock %p, net %p\n",
- namespace, ss7_ns_table[idx].ni_refcount, ss7_ns_table[idx].ni_sock,
- ss7_ns_table[idx].ni_net_ns);
-}
-
-void
-ss7_net_ns_put(unsigned int namespace)
-{
- struct ss7_ns_info *ni;
- unsigned int idx = SS7_NET_NS_IDX(namespace);
-
- if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
- /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
- return;
- ni = ss7_ns_table + idx;
-
- ss7_trace_printf(0, "ss7_net_ns_put(%x): refcount %d, sock %p, net %p\n",
- namespace, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
-
- ss7_mutex_enter(&ss7_glue_mutex);
- if (ni->ni_refcount && !--ni->ni_refcount) {
- /* Last reference gone */
- sock_release(ni->ni_sock);
- ni->ni_net_ns = NULL;
- ni->ni_sock = NULL;
- }
- ss7_mutex_exit(&ss7_glue_mutex);
-}
-
-static void
-ss7_net_ns_unload(void)
-{
- unsigned int idx;
- struct ss7_ns_info *ni;
-
- for (idx = 1; idx < ARRAY_SIZE(ss7_ns_table); idx++) {
- ni = ss7_ns_table + idx;
- if (!ni->ni_sock)
- continue;
-
- /* This should only report anything for the 'daemon' slot */
- printk(KERN_INFO "ss7_net_ns_unload(): idx %d, refcount %d, sock %p, net %p\n",
- idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
- sock_release(ni->ni_sock);
- ni->ni_net_ns = NULL;
- ni->ni_sock = NULL;
- ni->ni_refcount = 0;
- }
-}
-
-unsigned int
-ss7_net_ns_set(unsigned int new_namespace, unsigned int old_namespace)
-{
- static unsigned int num_used_idx = 2;
- unsigned int idx, free_idx;
- struct ss7_ns_info *ni;
- struct net *net;
-
- /* The new_namespace should have the low 16 bits zero.
- * The low bits of old_namespace indicate what was actually being used. */
-
- if (new_namespace != SS7_NET_NS_START) {
- ss7_net_ns_put(old_namespace);
- return new_namespace == SS7_NET_NS_DAEMON ? SS7_NET_NS_DAEMON : SS7_NET_NS_INIT;
- }
-
- /* SS7_NET_NS_START - look for an entry for the namespace of the current
- * process (which will be 'ss7maint start'). */
- net = current->nsproxy->net_ns;
-
- idx = SS7_NET_NS_IDX(old_namespace);
- ni = ss7_ns_table + idx;
- if (ni->ni_net_ns == net)
- /* Unchanged index, no need to change reference count */
- return SS7_NET_NS_START | idx;
-
- /* Different slot needed, drop old reference */
- ss7_net_ns_put(old_namespace);
-
- /* Check init and daemon entries, neither goes away */
- if (idx != SS7_NET_NS_IDX(SS7_NET_NS_INIT)
- && net == &init_net)
- return SS7_NET_NS_START | SS7_NET_NS_IDX(SS7_NET_NS_INIT);
-
- idx = SS7_NET_NS_IDX(SS7_NET_NS_DAEMON);
- ni = ss7_ns_table + idx;
- if (net == ni->ni_net_ns)
- return SS7_NET_NS_START | idx;
-
- ss7_mutex_enter(&ss7_glue_mutex);
-
- /* Scan table for an existing reference */
- free_idx = 0;
- for (idx = 2; idx < num_used_idx; idx++) {
- ni = ss7_ns_table + idx;
- if (ni->ni_net_ns == net) {
- /* found a match */
- ni->ni_refcount++;
- ss7_mutex_exit(&ss7_glue_mutex);
- ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): found idx %d, refcount %d, sock %p, net %p\n",
- new_namespace, old_namespace, idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
- return SS7_NET_NS_START | idx;
- }
- if (!free_idx && !ni->ni_net_ns)
- free_idx = idx;
- }
-
- /* Not found allocate lowest free slot */
- if (!free_idx) {
- if (num_used_idx >= ARRAY_SIZE(ss7_ns_table))
- /* Table full, borked */
- goto no_ref;
- free_idx = num_used_idx++;
- }
-
- ni = &ss7_ns_table[free_idx];
- ni->ni_sock = ss7_glue_create_ns_socket(net);
- if (!ni->ni_sock)
- goto no_ref;
- ni->ni_net_ns = net;
-
- ss7_mutex_exit(&ss7_glue_mutex);
- ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): new idx %d, sock %p, net %p\n",
- new_namespace, old_namespace, free_idx, ni->ni_sock, ni->ni_net_ns);
-
- return SS7_NET_NS_START | free_idx;
-
- no_ref:
- ss7_mutex_exit(&ss7_glue_mutex);
- ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): no_ref\n",
- new_namespace, old_namespace);
- return SS7_NET_NS_START;
-}
-
-void
-ss7_glue_daemon_open(void)
-{
- struct ss7_ns_info *ni = &ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_DAEMON)];
- struct net *net = current->nsproxy->net_ns;
-
- /* Save (and reference count) the network namespace the ss7 daemon
- * is started in. */
-
- /* Initialise the entry for init_net here - has to be done somewhere. */
- ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_INIT)].ni_net_ns = &init_net;
-
- if (net == ni->ni_net_ns)
- /* Unchanged */
- return;
-
- if (ni->ni_sock)
- sock_release(ni->ni_sock);
- ni->ni_sock = NULL;
-
- if (net != &init_net && !((ni->ni_sock = ss7_glue_create_ns_socket(net))))
- /* Can't create socket, default to global namespace */
- net = &init_net;
-
- ni->ni_net_ns = net;
-}
-
-int
-ss7_socket(int family, int type, int protocol, unsigned int namespace, struct socket **sockp)
-{
- struct socket *sock;
- struct net *net;
- unsigned int one = 1U;
- int rval;
-
- net = ss7_ns_table[SS7_NET_NS_IDX(namespace)].ni_net_ns;
- if (!net)
- net = &init_net;
-
- /* If we have to autoload the sctp module, we might re-enter it
- * before it has finished initialising - might go 'boom'. */
- ss7_mutex_enter(&ss7_glue_mutex);
-
- /* sock_create_kern() creates a socket that doesn't hold a reference
- * to the namespace (they get used for sockets needed by the protocol
- * stack code itself).
- * We need a socket that holds a reference to the namespace, so create
- * a 'user' socket in a specific namespace.
- * This adds an extra security check which we should pass because all the
- * sockets are created by kernel threads.
- */
- rval = __sock_create(net, family, type, protocol, sockp, 0);
- ss7_mutex_exit(&ss7_glue_mutex);
- if (rval != 0)
- return rval;
- sock = *sockp;
-
- kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
-
- return 0;
-}
-
-void
-ss7_setsockopt_nodelay(struct socket *sock, int enabled)
-{
- kernel_setsockopt(sock, SK_PROTOCOL(sock),
- SK_PROTOCOL(sock) == IPPROTO_TCP ? TCP_NODELAY : SCTP_NODELAY,
- &enabled, sizeof enabled);
-}
-
-static void
-ss7_sctp_set_opts(struct socket *sock)
-{
- struct sctp_event_subscribe events;
- int len, rval;
-
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return;
-
- len = sizeof events;
- rval = kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, &len);
- if (rval != 0)
- return;
-
- /* We need to know the stream and ppid */
- events.sctp_data_io_event = 1;
- /* Enable notifications to detect connection restart */
- events.sctp_association_event = 1;
- kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof events);
-}
-
-unsigned int
-ss7_get_max_sctp_ostreams(struct socket *sock)
-{
- struct sctp_status sstat;
- int len;
-
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return 0;
-
- len = sizeof sstat;
- if (kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &sstat, &len))
- return 0;
-
- return sstat.sstat_outstrms;
-}
-
-void
-ss7_set_max_sctp_streams(struct socket *sock, unsigned int max_streams)
-{
- struct sctp_initmsg sinit;
-
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return;
-
- memset(&sinit, 0, sizeof sinit);
-
- sinit.sinit_num_ostreams = max_streams;
- sinit.sinit_max_instreams = max_streams;
- kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_INITMSG, &sinit, sizeof sinit);
-}
-
-void
-ss7_trans_setsockopt(struct socket *sock)
-{
- unsigned int one = 1U;
-
- ss7_setsockopt_nodelay(sock, 1);
- ss7_sctp_set_opts(sock);
- if (SK_PROTOCOL(sock) == IPPROTO_TCP)
- kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
-}
-
-void
-ss7_transbind_setsockopt(struct socket *sock)
-{
- /* Set options for a listening socket */
- ss7_sctp_set_opts(sock);
-
- /* M3UA may need 16 data streams, it is just TFH to configure this */
- ss7_set_max_sctp_streams(sock, 1 + 16);
-}
-
-#define IP_ADDR_LEN(sa) ((sa)->sin6_family == AF_INET6 ? sizeof *(sa) : 16)
-int
-ss7_connect(struct socket *sock, struct sockaddr_in6 *sa)
-{
- return kernel_connect(sock, (void *)sa, IP_ADDR_LEN(sa), O_RDWR);
-}
-
-int
-ss7_bind(struct socket *sock, struct sockaddr_in6 *sa, unsigned int af_opts)
-{
- /* If we are binding INADDR6_ANY to an IPv6 socket (typically for
- * a listening socket) then we probably want to ensure that IPV6_V6ONLY
- * is 0 so that the socket will also be given IPv4 connections. */
- if (sa->sin6_family == AF_INET6 && af_opts & SS7_AF_OPT_IPv6_V6ONLY
- && sa->sin6_addr.in6_u.u6_addr32[0] == 0
- && (sa->sin6_addr.in6_u.u6_addr32[1]
- | sa->sin6_addr.in6_u.u6_addr32[2]
- | sa->sin6_addr.in6_u.u6_addr32[3]) == 0) {
- int v6only = af_opts & 1;
- kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof v6only);
- }
-
- return kernel_bind(sock, (void *)sa, IP_ADDR_LEN(sa));
-}
-
-int
-ss7_bindx(struct socket *sock, struct sockaddr_in6 *sa)
-{
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return -EPROTONOSUPPORT;
-
- return kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_SOCKOPT_BINDX_ADD,
- sa, IP_ADDR_LEN(sa));
-}
-
-int
-ss7_listen(struct socket *sock, int len)
-{
- return kernel_listen(sock, len);
-}
-
-int
-ss7_accept(struct socket *sock, struct socket **new_sockp, int flags)
-{
- return kernel_accept(sock, new_sockp, flags);
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
-static inline int
-ss7_kernel_getsockname(struct socket *sock, struct sockaddr *address)
-{
- int err, len;
-
- err = kernel_getsockname(sock, (struct sockaddr *)address, &len);
- return err ? err : len;
-}
-#define kernel_getsockname ss7_kernel_getsockname
-
-static inline int
-ss7_kernel_getpeername(struct socket *sock, struct sockaddr *address)
-{
- int err, len;
-
- err = kernel_getpeername(sock, (struct sockaddr *)address, &len);
- return err ? err : len;
-}
-#define kernel_getpeername ss7_kernel_getpeername
-#endif
-
-int
-ss7_get_loc_port(struct socket *sock)
-{
- char address[128 /*MAX_SOCK_ADDR*/];
- int len;
-
- len = kernel_getsockname(sock, (struct sockaddr *)address);
- if (len < 0)
- return 0;
-
- /* This works well enough for IPv4 and IPv6 */
- return ntohs(((struct sockaddr_in *)address)->sin_port);
-}
-
-int
-ss7_get_rem_addr(struct socket *sock, struct sockaddr_in6 *saddr)
-{
- int len;
-
- len = kernel_getpeername(sock, (struct sockaddr *)saddr);
- if (len < 0)
- return len;
-
- if (len > sizeof *saddr)
- printk(KERN_EMERG "ss7server: socket address (family %d) %d > %d",
- saddr->sin6_family, len, (int)sizeof *saddr);
-
- return 0;
-}
-
-int
-ss7_shutdown(struct socket *sock, int how)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
- if (SK_PROTOCOL(sock) == IPPROTO_SCTP) {
- struct linger linger;
-
- /* If we call kernel_sock_shutdown() then the connection isn't released
- * until all outstanding data has been acked.
- * If the remote system sends an INIT (restarting the connection)
- * while the linux kernel is waiting for data to be acked then it
- * will never disconnect.
- * Enabling 'linger' with a delay of zero causes sock_release()
- * to abort the connection (sends an ABORT chunk).
- *
- * The ss7 code never needs to wait for sent data to be acked,
- * so aborting the connection doesn't really matter.
- * All calls to ss7_shutdown() are immediately followed by calls to
- * ss7_closesocket().
- *
- * Plausibly we should always abort connections if we are disconnecting
- * due to an application level timeout.
- *
- * Fixed by the kernel patch:
- * "sctp: handle association restarts when the socket is closed"
- * Known to be included in the following kernels:
- * - mainline 3.18
- * - Ubuntu 3.13.11.11
- * Queued for 3.10-stable, 3.14-stable, 3.16-stable and 3.17-stable
- */
-
- linger.l_onoff = 1;
- linger.l_linger = 0;
- kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof linger);
-
- return 0;
- }
-#endif
- return kernel_sock_shutdown(sock, how);
-}
-
-void
-ss7_closesocket(struct socket *sock)
-{
- sock_release(sock);
-}
-
-int
-ss7_send(struct socket *sock, struct ss7_iovec *iov, int iovlen, int totlen,
- void *ctl, int ctl_len, unsigned int flags)
-{
- struct msghdr msg;
-
- msg.msg_name = 0;
- msg.msg_namelen = 0;
- msg.msg_control = ctl;
- msg.msg_controllen = ctl_len;
- msg.msg_flags = flags | MSG_NOSIGNAL;
-
- return kernel_sendmsg(sock, &msg, iov, iovlen, totlen);
-}
-
-int
-ss7_recv(struct socket *sock, unsigned char *data, int length, int flags)
-{
- struct kvec iov;
- struct msghdr msg;
-
- if (!sock->sk)
- return 0;
-
- iov.iov_len = length;
- iov.iov_base = data;
-
- msg.msg_name = 0;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
-
- return kernel_recvmsg(sock, &msg, &iov, 1, length, 0);
-}
-
-int
-ss7_recv_sctp(struct socket *sock, void *buf_1, int len_1, void *buf_2,
- int len_2, struct ss7_msgb *ss7_msg)
-{
- struct msghdr msg;
- struct kvec iov[2];
- unsigned char *data = buf_1;
- int msg_len, ctl_len;
- int rval;
- union {
- struct cmsghdr cmsg;
- unsigned int buf[16];
- } ctlbuf;
-
- if (!sock->sk)
- return 0;
-
- /* For SCTP each recvmsg should give us a single data record.
- * Since we only ever send SIGTRAN encoded messages bytes 4-7 are the
- * length - and should match that of the sctp data chunk.
- * buf_1/len_1 refer to the normal ss7 message buffer area, buf_2/len_2
- * are per-socket. Long messages get copied together by the caller.
- * The result is always a single valid SIGTRAN message */
-
- iov[0].iov_base = buf_1;
- iov[0].iov_len = len_1;
- iov[1].iov_base = buf_2;
- iov[1].iov_len = len_2;
-
- msg.msg_name = 0;
- msg.msg_namelen = 0;
- msg.msg_control = &ctlbuf;
- msg.msg_controllen = sizeof ctlbuf;
- msg.msg_flags = 0;
-
- rval = kernel_recvmsg(sock, &msg, iov, 2, len_1 + len_2, 0);
-
- if (rval <= 0)
- /* Don't return EBADMSG here */
- return rval != -EBADMSG ? rval : -EIO;
-
- if (msg.msg_flags & MSG_NOTIFICATION)
- /* msg data is a notification */
- return -EBADMSG;
-
- ctl_len = (char *)msg.msg_control - (char *)&ctlbuf;
- if (ctl_len >= ctlbuf.cmsg.cmsg_len
- && ctlbuf.cmsg.cmsg_level == IPPROTO_SCTP
- && ctlbuf.cmsg.cmsg_type == SCTP_SNDRCV) {
- struct sctp_sndrcvinfo *sinfo = CMSG_DATA(&ctlbuf.cmsg);
- ss7_trans_set_msg_info(ss7_msg, sinfo->sinfo_stream, sinfo->sinfo_ppid);
- }
-
- msg_len = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
- if (msg_len >= 65556)
- /* Disbelieve this is valid data */
- return -EIO;
-
- if (rval != msg_len || !(msg.msg_flags & MSG_EOR))
- return -EIO;
- return rval;
-}
-
-int
-ss7_trans_init_sctp_sinfo(void *buf, int maxlen, __u16 **stream, __u32 **ppid)
-{
- struct cmsghdr *cmsg;
- struct sctp_sndrcvinfo *sinfo;
-
- if (maxlen < CMSG_LEN(sizeof *sinfo))
- return -1;
-
- cmsg = buf;
- cmsg->cmsg_level = IPPROTO_SCTP;
- cmsg->cmsg_type = SCTP_SNDRCV;
- cmsg->cmsg_len = CMSG_LEN(sizeof *sinfo);
- sinfo = CMSG_DATA(cmsg);
- memset(sinfo, 0, sizeof *sinfo);
- *stream = &sinfo->sinfo_stream;
- *ppid = &sinfo->sinfo_ppid;
-
- return CMSG_LEN(sizeof *sinfo);
-}
\ No newline at end of file
diff --git a/a/content_digest b/N3/content_digest
index 444140f..30e6b45 100644
--- a/a/content_digest
+++ b/N3/content_digest
@@ -23,7 +23,7 @@
"From\0David Laight <David.Laight\@aculab.com>\0"
]
[
- "Subject\0RE: [Ocfs2-devel] [PATCH 27/33] sctp: export sctp_setsockopt_bindx\0"
+ "Subject\0[Ocfs2-devel] [PATCH 27/33] sctp: export sctp_setsockopt_bindx\0"
]
[
"Date\0Sun, 17 May 2020 08:48:02 +0000\0"
@@ -46,22 +46,10 @@
" linux-rdma\@vger.kernel.org <linux-rdma\@vger.kernel.org>",
" cluster-devel\@redhat.com <cluster-devel\@redhat.com>",
" Jakub Kicinski <kuba\@kernel.org>",
- " linux-block\@vger.kernel.org <linux-block\@vger.kernel.org>",
- " Alexey Kuznetsov <kuznet\@ms2.inr.ac.ru>",
- " ceph-devel\@vger.kernel.org <ceph-devel\@vger.kernel.org>",
- " linux-nfs\@vger.kernel.org <linux-nfs\@vger.kernel.org>",
- " Neil Horman <nhorman\@tuxdriver.com>",
- " Hideaki YOSHIFUJI <yoshfuji\@linux-ipv6.org>",
- " netdev\@vger.kernel.org <netdev\@vger.kernel.org>",
- " Vlad Yasevich <vyasevich\@gmail.com>",
- " Eric Dumazet <edumazet\@google.com>",
- " Jon Maloy <jmaloy\@redhat.com>",
- " Ying Xue <ying.xue\@windriver.com>",
- " David S. Miller <davem\@davemloft.net>",
- " ocfs2-devel\@oss.oracle.com <ocfs2-devel\@oss.oracle.com>\0"
+ " linux-block\@vger.kernel\0"
]
[
- "\0001:1\0"
+ "\0000:1\0"
]
[
"b\0"
@@ -110,21 +98,8 @@
"\n",
"-\n",
"Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK\n",
- "Registration No: 1397386 (Wales)"
-]
-[
- "\0001:2\0"
-]
-[
- "fn\0ss7osglue.c\0"
-]
-[
- "d\0ss7osglue.c\0"
-]
-[
- "b\0"
-]
-[
+ "Registration No: 1397386 (Wales)\n",
+ "-------------- next part --------------\n",
"#ident \"\@(#) (c) Aculab plc \$Header: /home/cvs/repository/ss7/stack/src/driver/linux/ss7osglue.c,v 1.157 2019-08-29 16:09:14 davidla Exp \$ \$Name: \$\"\n",
"#ifndef MODULE\n",
"#define MODULE\n",
@@ -1508,4 +1483,4 @@
"}"
]
-049f0d794ae7b86fe86338ebd5f3cc0175a0cd041f88b4d1c5c988d6b0c24e31
+25c68a2bdd36dd5ed3a5ba70fce070b25a567cfe82e0d08e685518b4a58c4a26
diff --git a/a/1.txt b/N4/1.txt
index ca5eed1..a418302 100644
--- a/a/1.txt
+++ b/N4/1.txt
@@ -41,4 +41,1386 @@ to support different and broken kernel releases.
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
-Registration No: 1397386 (Wales)
\ No newline at end of file
+Registration No: 1397386 (Wales)
+-------------- next part --------------
+#ident "@(#) (c) Aculab plc $Header: /home/cvs/repository/ss7/stack/src/driver/linux/ss7osglue.c,v 1.157 2019-08-29 16:09:14 davidla Exp $ $Name: $"
+#ifndef MODULE
+#define MODULE
+#endif
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+#error minimum kernel version is 2.6.28
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+#include <generated/autoconf.h>
+#else
+#include <linux/autoconf.h>
+#endif
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kmod.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/signal.h>
+#endif
+#include <linux/wait.h>
+#include <linux/socket.h>
+#include <linux/signal.h>
+#include <linux/poll.h>
+#include <linux/net.h>
+#include <linux/nsproxy.h>
+#include <linux/in.h>
+#include <linux/reboot.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include <linux/kthread.h>
+
+/* This is only in the kernel build tree */
+#include <net/sock.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+#include <uapi/linux/sctp.h>
+#else
+#include <net/sctp/user.h> /* netinet/sctp.h ought to be this file */
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
+#define wait_queue_head __wait_queue_head
+#define wait_queue_entry __wait_queue
+#endif
+
+#define SK_PROTOCOL(sock) (sock)->sk->sk_protocol
+
+extern void ss7_trace_mem(int, void *, int, const char *, ...);
+extern void ss7_trace_printf(int, const char *, ...);
+
+/* Aculab DACP interfaces - these are in aculab's kern_if.h */
+void *dacp_symbol_get(const char *);
+int dacp_symbol_release(const char *);
+
+MODULE_AUTHOR("Aculab");
+MODULE_LICENSE("Proprietary");
+
+#include "ss7osglue.h"
+
+/* Mutex for driver interface code */
+static struct mutex ss7_glue_mutex;
+
+static int ss7dev_major;
+static const void *ss7_dtls_handle;
+static int ss7_use_count;
+static int ss7_stop_pid;
+
+static struct task_struct *asserted_tasks[16];
+static unsigned int asserted_task_count;
+
+typedef char ss7_verify_const[ SS7_SOCK_STREAM == SOCK_STREAM && SS7_SOCK_SEQPACKET == SOCK_SEQPACKET ? 1 : -1];
+
+static void ss7_net_ns_unload(void);
+
+#define TCP_NODELAY 1
+
+static int ss7_glue_open(struct inode *, struct file *);
+static int ss7_glue_release(struct inode *, struct file *);
+static long ss7_glue_unlocked_ioctl(struct file *, unsigned int, unsigned long);
+static unsigned int ss7_glue_poll(struct file *const, poll_table *);
+
+static struct file_operations ss7dev_fop =
+{
+ open: ss7_glue_open,
+ release: ss7_glue_release,
+ unlocked_ioctl: ss7_glue_unlocked_ioctl,
+ compat_ioctl: ss7_glue_unlocked_ioctl,
+ poll: ss7_glue_poll,
+ owner: THIS_MODULE
+};
+
+static int ss7_reboot_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ /* System being rebooted.
+ * I added this hoping to use it to get the ss7maint daemon to exit,
+ * but it isn't called until all user processes have died.
+ * Leave it here - might be useful one day. */
+ return 0;
+}
+
+static struct notifier_block ss7_reboot_notifier_block = {
+ .notifier_call = ss7_reboot_notify,
+};
+
+static int
+ss7_init_fail(int rval)
+{
+ if (ss7dev_major > 0)
+ unregister_chrdev(ss7dev_major, "ss7server");
+ return rval;
+}
+
+static int
+ss7_init_mod(void)
+{
+ const void *(*dtls_register)(const char *, int (*)(struct dtls_get_if *));
+ int rval;
+
+ ss7_mutex_init(&ss7_glue_mutex);
+
+ printk(KERN_INFO "%s\n", ss7version);
+
+ ss7dev_major = register_chrdev(0, "ss7server", &ss7dev_fop);
+
+ if (ss7dev_major < 0) {
+ printk(KERN_INFO "ss7server: register_chrdev() failed: %d\n",
+ ss7dev_major);
+ return ss7_init_fail(ss7dev_major);
+ }
+
+ rval = ss7_driver_init();
+ if (rval != 0) {
+ printk(KERN_INFO "ss7server: ss7_driver_init() failed: %d\n", rval);
+ return ss7_init_fail(-EIO);
+ }
+
+ dtls_register = dacp_symbol_get("acuc_dtls_register");
+ if (dtls_register == NULL)
+ printk(KERN_INFO "ss7server: cannot locate \"acuc_dtls_register\"\n");
+ else
+ ss7_dtls_handle = dtls_register(DYNAMIC_TLS_PREFIX "ss7",
+ ss7_tls_get_if);
+
+ register_reboot_notifier(&ss7_reboot_notifier_block);
+ return 0;
+}
+
+static void
+ss7_cleanup_mod(void)
+{
+ int (*dtls_unregister)(const void *);
+
+ unregister_reboot_notifier(&ss7_reboot_notifier_block);
+
+ if (ss7_dtls_handle != NULL) {
+ dtls_unregister = dacp_symbol_get("acuc_dtls_unregister");
+ dacp_symbol_release("acuc_dtls_register");
+ if (dtls_unregister != NULL) {
+ dtls_unregister(ss7_dtls_handle);
+ dacp_symbol_release("acuc_dtls_unregister");
+ }
+ }
+
+ ss7_init_fail(0);
+
+ printk(KERN_INFO "Aculab ss7server: driver unloaded\n");
+}
+
+module_init(ss7_init_mod)
+module_exit(ss7_cleanup_mod)
+
+static int
+ss7_glue_open(struct inode *const inode, struct file *const filp)
+{
+ int rval, pid;
+
+ if (filp->private_data)
+ /* Duplicate open */
+ return 0;
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ if (ss7_use_count < 0) {
+ /* ss7_driver_shutdown() has been called, to late to do anything */
+ ss7_mutex_exit(&ss7_glue_mutex);
+ return -EIO;
+ }
+ ss7_use_count++;
+ ss7_mutex_exit(&ss7_glue_mutex);
+
+ rval = ss7_devif_open(&filp->private_data);
+ if (rval != 0) {
+ ss7_mutex_enter(&ss7_glue_mutex);
+ ss7_use_count--;
+ ss7_mutex_exit(&ss7_glue_mutex);
+ pid = ss7_pid();
+ if (pid != ss7_stop_pid)
+ printk(KERN_INFO "ss7_devif_open() pid %d failed ss7 error %d\n",
+ pid, rval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+ss7_glue_release(struct inode *const inode, struct file *const filp)
+{
+ if (filp->private_data)
+ ss7_devif_close(filp->private_data);
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ ss7_use_count--;
+
+ if (ss7_use_count == 0 && ss7_stop_pid != 0) {
+ /* Last user process has gone, complete shutdown functions */
+ ss7_net_ns_unload();
+ /* Stop any more opens */
+ ss7_use_count = -1;
+ ss7_driver_shutdown();
+ }
+
+ ss7_mutex_exit(&ss7_glue_mutex);
+
+ return 0;
+}
+
+static long
+ss7_glue_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ if (!filp->private_data)
+ return -ENODEV;
+
+ switch (cmd) {
+
+ case SS7_STOP: /* ss7maint shutting us down */
+ /* Start shutdown now, will complete on last close */
+ ss7_driver_stop();
+ ss7_stop_pid = ss7_pid();
+ return 0;
+
+ /* Request from ss7maint or user application */
+ case SS7_USER_IOCTL_CODE:
+ return ss7dev_ioctl(filp->private_data, cmd, arg);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static unsigned int
+ss7_glue_poll(struct file *filp, poll_table *pt)
+{
+ poll_wait(filp, *ss7_devif_get_pollqueue_head(filp->private_data), pt);
+ return ss7_devif_get_poll_status(filp->private_data);
+}
+
+void *
+ss7_os_malloc(int s, int ss7_flags)
+{
+ return kmalloc(s, GFP_KERNEL);
+}
+
+void
+ss7_os_free(void *p)
+{
+ kfree(p);
+}
+
+void
+ss7_poll_queue_head_deinit(wait_queue_head_t **pqhp)
+{
+ ss7_os_free(*pqhp);
+}
+
+int
+ss7_poll_queue_head_init(wait_queue_head_t **pqhp)
+{
+ wait_queue_head_t *pqh = ss7_os_malloc(sizeof *pqh, 0);
+ if (pqh == NULL)
+ return -1;
+ init_waitqueue_head(pqh);
+ *pqhp = pqh;
+ return 0;
+}
+
+void
+ss7_pollwakeup(wait_queue_head_t **pqh, unsigned int poll_event)
+{
+ wake_up(*pqh);
+}
+
+void
+ss7_kill_task(struct task_struct *task, int signo)
+{
+ /* Send signal even though set to SIG_IGN */
+ force_sig(signo, task);
+}
+
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
+/* spinlock_t is a typedef for an unnamed structure so we can't
+ * make 'struct spinlock' match the kernel spinlock type. */
+#define SPINLOCK_CAST (spinlock_t *)
+#else
+#define SPINLOCK_CAST
+#endif
+
+size_t
+ss7_spin_lock_size(void)
+{
+ return sizeof *SPINLOCK_CAST(struct spinlock *)0;
+}
+
+void
+ss7_spin_lock_init(struct spinlock *s)
+{
+ spin_lock_init(SPINLOCK_CAST s);
+}
+
+void
+ss7_spin_lock_enter(struct spinlock *s)
+{
+ spin_lock(SPINLOCK_CAST s);
+}
+
+void
+ss7_spin_lock_exit(struct spinlock *s)
+{
+ spin_unlock(SPINLOCK_CAST s);
+}
+
+size_t
+ss7_mutex_size(void)
+{
+ return sizeof(struct mutex);
+}
+
+void
+ss7_mutex_init(struct mutex *s)
+{
+ mutex_init(s);
+}
+
+void
+ss7_mutex_enter(struct mutex *s)
+{
+ mutex_lock(s);
+}
+
+int
+ss7_mutex_enter_tmo(struct mutex *s, int max_wait)
+{
+ /* There is no mutex_enter_timeout() however this was all added
+ * to stop status commands sleeping forever when a process has
+ * 'oopsed' with a mutex held.
+ * Do a sneak check on the state of any owning task then
+ * wait interruptibly.
+ * ^C should error out the status call. */
+
+ /* If uncontended just acquire */
+ if (mutex_trylock(s))
+ return 1;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
+ {
+ struct task_struct *owner;
+ int state;
+
+ spin_lock(&s->wait_lock);
+ owner = __mutex_owner(s);
+ state = owner ? owner->state : 0;
+ spin_unlock(&s->wait_lock);
+ if (state & TASK_DEAD)
+ /* mutex will never be released, treat as timeout */
+ return 0;
+ }
+#endif
+
+ /* If C7_ASSERT() has been called, just let everyone in */
+ if (asserted_task_count)
+ return 0;
+
+ return mutex_lock_interruptible(s) ? -1 /* EINTR */ : 1 /* acquired */;
+}
+
+void
+ss7_mutex_exit(struct mutex *s)
+{
+ mutex_unlock(s);
+}
+
+size_t
+ss7_cv_size(void)
+{
+ return sizeof(wait_queue_head_t);
+}
+
+void
+ss7_cv_init(wait_queue_head_t *const v)
+{
+ init_waitqueue_head(v);
+}
+
+static int
+ss7_schedule_tmo(int tmo_ms)
+{
+ int tmo_jiffies;
+
+ /* Really sleep - unless woken since unlocking spinlock */
+ if (tmo_ms >= 0) {
+ if (tmo_ms <= 1)
+ tmo_jiffies = tmo_ms;
+ else
+ /* Convert to jiffies and round up */
+ tmo_jiffies = 1 + (tmo_ms + 1 - 1) * 16 / (16000/HZ);
+ /* Return value of schedule_timeout() is unexpired timeout */
+ /* We want 0 for 'timedout' (to match cv_wait_sig()) */
+ return schedule_timeout(tmo_jiffies) != 0;
+ }
+
+ schedule();
+ if (!signal_pending(current))
+ /* Woken by the event */
+ return 1;
+
+ /* Report 0 for a signal, except -1 for SIGKILL (reboot) */
+ return sigismember(¤t->pending.signal, SIGKILL) ? -1 : 0;
+}
+
+int
+ss7_cv_wait_guts(wait_queue_head_t *cvp, struct mutex *mtxp,
+ int interruptible, int tmo_ms)
+{
+ int r;
+ struct wait_queue_entry w;
+ int sleep_state;
+
+ init_waitqueue_entry(&w, current);
+
+ /* Tell scheduler we are going to sleep... */
+ if (signal_pending(current) && !interruptible)
+ /* We don't want waking immediately (again) */
+ sleep_state = TASK_UNINTERRUPTIBLE;
+ else
+ sleep_state = TASK_INTERRUPTIBLE;
+ set_current_state(sleep_state);
+
+ /* Connect to condition variable ... */
+ add_wait_queue(cvp, &w);
+ mutex_unlock(mtxp); /* Release mutex */
+
+ r = ss7_schedule_tmo(tmo_ms);
+
+ /* Disconnect from condition variable ... */
+ remove_wait_queue(cvp, &w);
+
+ /* Re-acquire mutex */
+ mutex_lock(mtxp);
+
+ /* return 1 if woken, 0 if timed_out/signal, -1 if SIGKILL */
+ return r;
+}
+
+int
+ss7_cv_wait_spin_lock(wait_queue_head_t *cvp, struct spinlock *lock,
+ int interruptible, int tmo_ms)
+{
+ int r;
+ struct wait_queue_entry w;
+ int sleep_state;
+
+ init_waitqueue_entry(&w, current);
+
+ /* Tell scheduler we are going to sleep... */
+ if (signal_pending(current) && !interruptible)
+ /* We don't want waking immediately (again) */
+ sleep_state = TASK_UNINTERRUPTIBLE;
+ else
+ sleep_state = TASK_INTERRUPTIBLE;
+ set_current_state(sleep_state);
+
+ /* Connect to condition variable ... */
+ add_wait_queue(cvp, &w);
+ spin_unlock(SPINLOCK_CAST lock);
+
+ r = ss7_schedule_tmo(tmo_ms);
+
+ /* Disconnect from condition variable ... */
+ remove_wait_queue(cvp, &w);
+
+ /* Re-acquire mutex */
+ spin_lock(SPINLOCK_CAST lock);
+
+ return r;
+}
+
+/*---------------------------------------------------------------------**
+** ss7_cv_broadcast **
+** Awaken all threads that are sleeping on a condition variable. **
+** Caller must use the associated mutex sensibly, i.e. ... **
+** acquire the mutex **
+** Set some flag that a sleeping thread will check for **
+** ss7_cv_broadcast() **
+** release the mutex **
+**---------------------------------------------------------------------*/
+
+void
+ss7_cv_broadcast(wait_queue_head_t *const cvp)
+{
+ wake_up(cvp);
+}
+
+
+unsigned long
+ss7_copy_to_user(void *to, const void *from, unsigned long c)
+{
+ return copy_to_user(to, from, c);
+}
+
+unsigned long
+ss7_copy_from_user(void *to, const void *from, unsigned long c)
+{
+ return copy_from_user(to, from, c);
+}
+
+unsigned int
+ss7_pid(void)
+{
+ return current->pid;
+}
+
+struct task_struct *
+ss7_current_task(void)
+{
+ return current;
+}
+
+unsigned int
+ss7_task_pid(struct task_struct *task)
+{
+ return task->pid;
+}
+
+int
+ss7_glue_thread_fn(void *ss7_thread)
+{
+ ss7_thread_run(ss7_thread);
+ module_put_and_exit(0);
+ return 0;
+}
+
+struct task_struct *
+ss7_os_thread_create(struct ss7_thread *thrp, const char *desc)
+{
+ struct task_struct *task;
+ const char *sp;
+ int len;
+
+ if (!try_module_get(THIS_MODULE))
+ return NULL;
+
+ /* The thread description gets truncated to 15 chars, can't be helped!
+ * Use 'ss7maint osstatus -t' to get the full description. */
+
+ /* Remove any leading space and truncate after second word */
+ if (desc[0] == ' ')
+ desc++;
+ len = 100;
+ sp = ss7strchr(desc, ' ');
+ if (sp != NULL) {
+ sp = ss7strchr(sp + 1, ' ');
+ if (sp != NULL)
+ len = sp - desc;
+ }
+
+ task = kthread_run(ss7_glue_thread_fn, thrp, "ss7:%.*s", len, desc);
+ if (IS_ERR(task)) {
+ module_put(THIS_MODULE);
+ return NULL;
+ }
+ return task;
+}
+
+void
+ss7_ms_delay(const unsigned int ms)
+{
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((unsigned long long)HZ * ms / 1000);
+}
+
+int
+ss7_os_get_ticks(void)
+{
+ return jiffies;
+}
+
+int
+ss7_os_ticks_to_us(int interval)
+{
+ return interval * 1000000 / HZ;
+}
+
+int
+ss7_os_ticks_to_ms(int interval)
+{
+ return interval * 1000 / HZ;
+}
+
+int
+ss7_os_ticks_to_secs(int interval)
+{
+ return interval / HZ;
+}
+
+unsigned int
+ss7_get_ms_time(void)
+{
+ static unsigned long epoch;
+ struct timespec now;
+
+ getrawmonotonic(&now);
+
+ if (epoch == 0)
+ epoch = now.tv_sec;
+
+ return (now.tv_sec - epoch) * 1000 + now.tv_nsec / 1000000;
+}
+
+struct acu_ss7maint_time {
+ unsigned int st_sec;
+ unsigned int st_usec;
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
+static inline void do_gettimeofday(struct timeval *tv)
+{
+ struct timespec64 ts;
+
+ ktime_get_real_ts64(&ts);
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec/1000u;
+}
+#endif
+
+void
+ss7_get_timestamp(struct acu_ss7maint_time *ptime)
+{
+ struct timeval tv;
+
+ /* do_gettimeofday() returns 'wall clock time'.
+ * It can go backwards. */
+ do_gettimeofday(&tv);
+ ptime->st_sec = tv.tv_sec;
+ ptime->st_usec = tv.tv_usec;
+}
+
+unsigned int
+ss7_get_elapsed(const struct acu_ss7maint_time *epoch)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+
+ return tv.tv_sec - epoch->st_sec;
+}
+
+void
+ss7_os_log_error(const char *text)
+{
+ printk(KERN_EMERG "ss7server: %s", text);
+ if (memcmp(text, "Assertion fail", 14) == 0) {
+ dump_stack();
+ /* Although we return, the caller sleeps forever */
+ /* Remember the 'stuck' tasks */
+ asserted_tasks[asserted_task_count++ & 15] = current;
+ }
+}
+
+/*---------------------------------------------------------------------**
+** Miscellanous string and memory functions **
+**---------------------------------------------------------------------*/
+
+void
+ss7memzero(void *buf, size_t len)
+{
+ memset(buf, 0, len);
+}
+
+void
+ss7memcpy(void *dest, const void *src, size_t len)
+{
+ memcpy(dest, src, len);
+}
+
+void
+ss7_memmove(void *dest, const void *src, size_t len)
+{
+ memmove(dest, src, len);
+}
+
+int
+ss7memcmp(const void *s1, const void *s2, size_t len)
+{
+ return memcmp(s1, s2, len);
+}
+
+unsigned int
+ss7strlen(const char *str)
+{
+ return strlen(str);
+}
+
+void
+ss7strcpy(char *dest, const char *src)
+{
+ strcpy(dest, src);
+}
+
+int
+ss7strcmp(const char *dest, const char *src)
+{
+ return strcmp(dest, src);
+}
+
+char *
+ss7strncpy(char *const s1, const char *s2, size_t n)
+{
+ return strncpy(s1, s2, n);
+}
+
+char *
+ss7strchr(const char *s, const int c)
+{
+ return strchr(s, c);
+}
+
+/*---------------------------------------------------------------------**
+** TCP/IP functions **
+**---------------------------------------------------------------------*/
+
+int
+ss7_sctp_supported(void)
+{
+ return 1;
+}
+
+unsigned int
+ss7_get_default_af_opts(unsigned int protocol, unsigned int port)
+{
+ /* The SS7 driver needs to know the which address families (IPv4 or IPv6)
+ * to use for listening sockets.
+ *
+ * Whether an IPV6 socket can accept IPV4 connections depends on
+ * the IPV6_V6ONLY socket option. The default for which depends
+ * on net.ipv6.bindv6only (which usually defaults to 0 - allowing IPV4).
+ * There also might be kernels where clearing IPV6_V6ONLY is disallowed.
+ *
+ * Normally only a single socket is created for each port since an IPv6
+ * socket can receive IPv4 connections. However a separate IPv4 socket
+ * can be requested.
+ *
+ * This function should return one of:
+ * SS7_AF_OPT_IPv6
+ * IPV6 socket with the default IPV6_V6ONLY value.
+ * SS7_AF_OPT_IPv6_V6ONLY_CLR
+ * IPV6 socket with IPV6_V6ONLY explicitly cleared.
+ * SS7_AF_OPT_IPv6_V6ONLY_SET
+ * IPV6 socket with IPV6_V6ONLY explicitly set.
+ * Possibly logically ored with:
+ * SS7_AF_OPT_IPv4
+ * A separate IPv4 socket.
+ *
+ * For flexibility the decision can be based on the protocol (either
+ * IPPROTO_SCTP or IPPROTO_TCP) or the port number.
+ *
+ * Default to creating a single socket and disabling IPV6_V6ONLY.
+ */
+#ifndef SS7_DEFAULT_AF_OPTS
+#define SS7_DEFAULT_AF_OPTS SS7_AF_OPT_IPv6
+#endif
+ return SS7_DEFAULT_AF_OPTS;
+}
+
+/* kernel_get/set_sockopt() prototypes have (char *) for the buffer.
+ * #define a (void *) cast.
+ */
+#define kernel_setsockopt(sock, level, name, val, len) \
+ kernel_setsockopt(sock, level, name, (void *)val, len)
+#define kernel_getsockopt(sock, level, name, val, len) \
+ kernel_getsockopt(sock, level, name, (void *)val, len)
+
+/* Note that we can't (easily) hold reference counts on the namespace
+ * because put_net() is GPL_ONLY.
+ * Instead we keep our own table and create a socket to hold the
+ * reference for us.
+ * Table entries 0 and 1 always refer to init_net and the namespace
+ * of the (last started) ss7 daemon. Neither is reference counted
+ * (although we hold a single reference on the latter).
+ * Higher entries are saved from invocations of 'ss7maint start'
+ * and 'firmware download'. */
+
+static struct ss7_ns_info {
+ struct net *ni_net_ns;
+ struct socket *ni_sock;
+ unsigned int ni_refcount;
+} ss7_ns_table[256];
+
+static struct socket *
+ss7_glue_create_ns_socket(struct net *net)
+{
+ struct socket *sock;
+
+ if (__sock_create(net, AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock, 0))
+ return NULL;
+ return sock;
+}
+
+void
+ss7_net_ns_get(unsigned int namespace)
+{
+ unsigned int idx = SS7_NET_NS_IDX(namespace);
+
+ if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
+ /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
+ return;
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ ss7_ns_table[idx].ni_refcount++;
+ ss7_mutex_exit(&ss7_glue_mutex);
+
+ ss7_trace_printf(0, "ss7_net_ns_get(%x): refcount %d, sock %p, net %p\n",
+ namespace, ss7_ns_table[idx].ni_refcount, ss7_ns_table[idx].ni_sock,
+ ss7_ns_table[idx].ni_net_ns);
+}
+
+void
+ss7_net_ns_put(unsigned int namespace)
+{
+ struct ss7_ns_info *ni;
+ unsigned int idx = SS7_NET_NS_IDX(namespace);
+
+ if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
+ /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
+ return;
+ ni = ss7_ns_table + idx;
+
+ ss7_trace_printf(0, "ss7_net_ns_put(%x): refcount %d, sock %p, net %p\n",
+ namespace, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+ if (ni->ni_refcount && !--ni->ni_refcount) {
+ /* Last reference gone */
+ sock_release(ni->ni_sock);
+ ni->ni_net_ns = NULL;
+ ni->ni_sock = NULL;
+ }
+ ss7_mutex_exit(&ss7_glue_mutex);
+}
+
+static void
+ss7_net_ns_unload(void)
+{
+ unsigned int idx;
+ struct ss7_ns_info *ni;
+
+ for (idx = 1; idx < ARRAY_SIZE(ss7_ns_table); idx++) {
+ ni = ss7_ns_table + idx;
+ if (!ni->ni_sock)
+ continue;
+
+ /* This should only report anything for the 'daemon' slot */
+ printk(KERN_INFO "ss7_net_ns_unload(): idx %d, refcount %d, sock %p, net %p\n",
+ idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
+ sock_release(ni->ni_sock);
+ ni->ni_net_ns = NULL;
+ ni->ni_sock = NULL;
+ ni->ni_refcount = 0;
+ }
+}
+
+unsigned int
+ss7_net_ns_set(unsigned int new_namespace, unsigned int old_namespace)
+{
+ static unsigned int num_used_idx = 2;
+ unsigned int idx, free_idx;
+ struct ss7_ns_info *ni;
+ struct net *net;
+
+ /* The new_namespace should have the low 16 bits zero.
+ * The low bits of old_namespace indicate what was actually being used. */
+
+ if (new_namespace != SS7_NET_NS_START) {
+ ss7_net_ns_put(old_namespace);
+ return new_namespace == SS7_NET_NS_DAEMON ? SS7_NET_NS_DAEMON : SS7_NET_NS_INIT;
+ }
+
+ /* SS7_NET_NS_START - look for an entry for the namespace of the current
+ * process (which will be 'ss7maint start'). */
+ net = current->nsproxy->net_ns;
+
+ idx = SS7_NET_NS_IDX(old_namespace);
+ ni = ss7_ns_table + idx;
+ if (ni->ni_net_ns == net)
+ /* Unchanged index, no need to change reference count */
+ return SS7_NET_NS_START | idx;
+
+ /* Different slot needed, drop old reference */
+ ss7_net_ns_put(old_namespace);
+
+ /* Check init and daemon entries, neither goes away */
+ if (idx != SS7_NET_NS_IDX(SS7_NET_NS_INIT)
+ && net == &init_net)
+ return SS7_NET_NS_START | SS7_NET_NS_IDX(SS7_NET_NS_INIT);
+
+ idx = SS7_NET_NS_IDX(SS7_NET_NS_DAEMON);
+ ni = ss7_ns_table + idx;
+ if (net == ni->ni_net_ns)
+ return SS7_NET_NS_START | idx;
+
+ ss7_mutex_enter(&ss7_glue_mutex);
+
+ /* Scan table for an existing reference */
+ free_idx = 0;
+ for (idx = 2; idx < num_used_idx; idx++) {
+ ni = ss7_ns_table + idx;
+ if (ni->ni_net_ns == net) {
+ /* found a match */
+ ni->ni_refcount++;
+ ss7_mutex_exit(&ss7_glue_mutex);
+ ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): found idx %d, refcount %d, sock %p, net %p\n",
+ new_namespace, old_namespace, idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
+ return SS7_NET_NS_START | idx;
+ }
+ if (!free_idx && !ni->ni_net_ns)
+ free_idx = idx;
+ }
+
+ /* Not found allocate lowest free slot */
+ if (!free_idx) {
+ if (num_used_idx >= ARRAY_SIZE(ss7_ns_table))
+ /* Table full, borked */
+ goto no_ref;
+ free_idx = num_used_idx++;
+ }
+
+ ni = &ss7_ns_table[free_idx];
+ ni->ni_sock = ss7_glue_create_ns_socket(net);
+ if (!ni->ni_sock)
+ goto no_ref;
+ ni->ni_net_ns = net;
+
+ ss7_mutex_exit(&ss7_glue_mutex);
+ ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): new idx %d, sock %p, net %p\n",
+ new_namespace, old_namespace, free_idx, ni->ni_sock, ni->ni_net_ns);
+
+ return SS7_NET_NS_START | free_idx;
+
+ no_ref:
+ ss7_mutex_exit(&ss7_glue_mutex);
+ ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): no_ref\n",
+ new_namespace, old_namespace);
+ return SS7_NET_NS_START;
+}
+
+void
+ss7_glue_daemon_open(void)
+{
+ struct ss7_ns_info *ni = &ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_DAEMON)];
+ struct net *net = current->nsproxy->net_ns;
+
+ /* Save (and reference count) the network namespace the ss7 daemon
+ * is started in. */
+
+ /* Initialise the entry for init_net here - has to be done somewhere. */
+ ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_INIT)].ni_net_ns = &init_net;
+
+ if (net == ni->ni_net_ns)
+ /* Unchanged */
+ return;
+
+ if (ni->ni_sock)
+ sock_release(ni->ni_sock);
+ ni->ni_sock = NULL;
+
+ if (net != &init_net && !((ni->ni_sock = ss7_glue_create_ns_socket(net))))
+ /* Can't create socket, default to global namespace */
+ net = &init_net;
+
+ ni->ni_net_ns = net;
+}
+
+int
+ss7_socket(int family, int type, int protocol, unsigned int namespace, struct socket **sockp)
+{
+ struct socket *sock;
+ struct net *net;
+ unsigned int one = 1U;
+ int rval;
+
+ net = ss7_ns_table[SS7_NET_NS_IDX(namespace)].ni_net_ns;
+ if (!net)
+ net = &init_net;
+
+ /* If we have to autoload the sctp module, we might re-enter it
+ * before it has finished initialising - might go 'boom'. */
+ ss7_mutex_enter(&ss7_glue_mutex);
+
+ /* sock_create_kern() creates a socket that doesn't hold a reference
+ * to the namespace (they get used for sockets needed by the protocol
+ * stack code itself).
+ * We need a socket that holds a reference to the namespace, so create
+ * a 'user' socket in a specific namespace.
+ * This adds an extra security check which we should pass because all the
+ * sockets are created by kernel threads.
+ */
+ rval = __sock_create(net, family, type, protocol, sockp, 0);
+ ss7_mutex_exit(&ss7_glue_mutex);
+ if (rval != 0)
+ return rval;
+ sock = *sockp;
+
+ kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
+
+ return 0;
+}
+
+void
+ss7_setsockopt_nodelay(struct socket *sock, int enabled)
+{
+ kernel_setsockopt(sock, SK_PROTOCOL(sock),
+ SK_PROTOCOL(sock) == IPPROTO_TCP ? TCP_NODELAY : SCTP_NODELAY,
+ &enabled, sizeof enabled);
+}
+
+static void
+ss7_sctp_set_opts(struct socket *sock)
+{
+ struct sctp_event_subscribe events;
+ int len, rval;
+
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return;
+
+ len = sizeof events;
+ rval = kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, &len);
+ if (rval != 0)
+ return;
+
+ /* We need to know the stream and ppid */
+ events.sctp_data_io_event = 1;
+ /* Enable notifications to detect connection restart */
+ events.sctp_association_event = 1;
+ kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof events);
+}
+
+unsigned int
+ss7_get_max_sctp_ostreams(struct socket *sock)
+{
+ struct sctp_status sstat;
+ int len;
+
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return 0;
+
+ len = sizeof sstat;
+ if (kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &sstat, &len))
+ return 0;
+
+ return sstat.sstat_outstrms;
+}
+
+void
+ss7_set_max_sctp_streams(struct socket *sock, unsigned int max_streams)
+{
+ struct sctp_initmsg sinit;
+
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return;
+
+ memset(&sinit, 0, sizeof sinit);
+
+ sinit.sinit_num_ostreams = max_streams;
+ sinit.sinit_max_instreams = max_streams;
+ kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_INITMSG, &sinit, sizeof sinit);
+}
+
+void
+ss7_trans_setsockopt(struct socket *sock)
+{
+ unsigned int one = 1U;
+
+ ss7_setsockopt_nodelay(sock, 1);
+ ss7_sctp_set_opts(sock);
+ if (SK_PROTOCOL(sock) == IPPROTO_TCP)
+ kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
+}
+
+void
+ss7_transbind_setsockopt(struct socket *sock)
+{
+ /* Set options for a listening socket */
+ ss7_sctp_set_opts(sock);
+
+ /* M3UA may need 16 data streams, it is just TFH to configure this */
+ ss7_set_max_sctp_streams(sock, 1 + 16);
+}
+
+#define IP_ADDR_LEN(sa) ((sa)->sin6_family == AF_INET6 ? sizeof *(sa) : 16)
+int
+ss7_connect(struct socket *sock, struct sockaddr_in6 *sa)
+{
+ return kernel_connect(sock, (void *)sa, IP_ADDR_LEN(sa), O_RDWR);
+}
+
+int
+ss7_bind(struct socket *sock, struct sockaddr_in6 *sa, unsigned int af_opts)
+{
+ /* If we are binding INADDR6_ANY to an IPv6 socket (typically for
+ * a listening socket) then we probably want to ensure that IPV6_V6ONLY
+ * is 0 so that the socket will also be given IPv4 connections. */
+ if (sa->sin6_family == AF_INET6 && af_opts & SS7_AF_OPT_IPv6_V6ONLY
+ && sa->sin6_addr.in6_u.u6_addr32[0] == 0
+ && (sa->sin6_addr.in6_u.u6_addr32[1]
+ | sa->sin6_addr.in6_u.u6_addr32[2]
+ | sa->sin6_addr.in6_u.u6_addr32[3]) == 0) {
+ int v6only = af_opts & 1;
+ kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof v6only);
+ }
+
+ return kernel_bind(sock, (void *)sa, IP_ADDR_LEN(sa));
+}
+
+int
+ss7_bindx(struct socket *sock, struct sockaddr_in6 *sa)
+{
+ if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
+ return -EPROTONOSUPPORT;
+
+ return kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_SOCKOPT_BINDX_ADD,
+ sa, IP_ADDR_LEN(sa));
+}
+
+int
+ss7_listen(struct socket *sock, int len)
+{
+ return kernel_listen(sock, len);
+}
+
+int
+ss7_accept(struct socket *sock, struct socket **new_sockp, int flags)
+{
+ return kernel_accept(sock, new_sockp, flags);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
+static inline int
+ss7_kernel_getsockname(struct socket *sock, struct sockaddr *address)
+{
+ int err, len;
+
+ err = kernel_getsockname(sock, (struct sockaddr *)address, &len);
+ return err ? err : len;
+}
+#define kernel_getsockname ss7_kernel_getsockname
+
+static inline int
+ss7_kernel_getpeername(struct socket *sock, struct sockaddr *address)
+{
+ int err, len;
+
+ err = kernel_getpeername(sock, (struct sockaddr *)address, &len);
+ return err ? err : len;
+}
+#define kernel_getpeername ss7_kernel_getpeername
+#endif
+
+int
+ss7_get_loc_port(struct socket *sock)
+{
+ char address[128 /*MAX_SOCK_ADDR*/];
+ int len;
+
+ len = kernel_getsockname(sock, (struct sockaddr *)address);
+ if (len < 0)
+ return 0;
+
+ /* This works well enough for IPv4 and IPv6 */
+ return ntohs(((struct sockaddr_in *)address)->sin_port);
+}
+
+int
+ss7_get_rem_addr(struct socket *sock, struct sockaddr_in6 *saddr)
+{
+ int len;
+
+ len = kernel_getpeername(sock, (struct sockaddr *)saddr);
+ if (len < 0)
+ return len;
+
+ if (len > sizeof *saddr)
+ printk(KERN_EMERG "ss7server: socket address (family %d) %d > %d",
+ saddr->sin6_family, len, (int)sizeof *saddr);
+
+ return 0;
+}
+
+int
+ss7_shutdown(struct socket *sock, int how)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
+ if (SK_PROTOCOL(sock) == IPPROTO_SCTP) {
+ struct linger linger;
+
+ /* If we call kernel_sock_shutdown() then the connection isn't released
+ * until all outstanding data has been acked.
+ * If the remote system sends an INIT (restarting the connection)
+ * while the linux kernel is waiting for data to be acked then it
+ * will never disconnect.
+ * Enabling 'linger' with a delay of zero causes sock_release()
+ * to abort the connection (sends an ABORT chunk).
+ *
+ * The ss7 code never needs to wait for sent data to be acked,
+ * so aborting the connection doesn't really matter.
+ * All calls to ss7_shutdown() are immediately followed by calls to
+ * ss7_closesocket().
+ *
+ * Plausibly we should always abort connections if we are disconnecting
+ * due to an application level timeout.
+ *
+ * Fixed by the kernel patch:
+ * "sctp: handle association restarts when the socket is closed"
+ * Known to be included in the following kernels:
+ * - mainline 3.18
+ * - Ubuntu 3.13.11.11
+ * Queued for 3.10-stable, 3.14-stable, 3.16-stable and 3.17-stable
+ */
+
+ linger.l_onoff = 1;
+ linger.l_linger = 0;
+ kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof linger);
+
+ return 0;
+ }
+#endif
+ return kernel_sock_shutdown(sock, how);
+}
+
+void
+ss7_closesocket(struct socket *sock)
+{
+ sock_release(sock);
+}
+
+int
+ss7_send(struct socket *sock, struct ss7_iovec *iov, int iovlen, int totlen,
+ void *ctl, int ctl_len, unsigned int flags)
+{
+ struct msghdr msg;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_control = ctl;
+ msg.msg_controllen = ctl_len;
+ msg.msg_flags = flags | MSG_NOSIGNAL;
+
+ return kernel_sendmsg(sock, &msg, iov, iovlen, totlen);
+}
+
+int
+ss7_recv(struct socket *sock, unsigned char *data, int length, int flags)
+{
+ struct kvec iov;
+ struct msghdr msg;
+
+ if (!sock->sk)
+ return 0;
+
+ iov.iov_len = length;
+ iov.iov_base = data;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ return kernel_recvmsg(sock, &msg, &iov, 1, length, 0);
+}
+
+int
+ss7_recv_sctp(struct socket *sock, void *buf_1, int len_1, void *buf_2,
+ int len_2, struct ss7_msgb *ss7_msg)
+{
+ struct msghdr msg;
+ struct kvec iov[2];
+ unsigned char *data = buf_1;
+ int msg_len, ctl_len;
+ int rval;
+ union {
+ struct cmsghdr cmsg;
+ unsigned int buf[16];
+ } ctlbuf;
+
+ if (!sock->sk)
+ return 0;
+
+ /* For SCTP each recvmsg should give us a single data record.
+ * Since we only ever send SIGTRAN encoded messages bytes 4-7 are the
+ * length - and should match that of the sctp data chunk.
+ * buf_1/len_1 refer to the normal ss7 message buffer area, buf_2/len_2
+ * are per-socket. Long messages get copied together by the caller.
+ * The result is always a single valid SIGTRAN message */
+
+ iov[0].iov_base = buf_1;
+ iov[0].iov_len = len_1;
+ iov[1].iov_base = buf_2;
+ iov[1].iov_len = len_2;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_control = &ctlbuf;
+ msg.msg_controllen = sizeof ctlbuf;
+ msg.msg_flags = 0;
+
+ rval = kernel_recvmsg(sock, &msg, iov, 2, len_1 + len_2, 0);
+
+ if (rval <= 0)
+ /* Don't return EBADMSG here */
+ return rval != -EBADMSG ? rval : -EIO;
+
+ if (msg.msg_flags & MSG_NOTIFICATION)
+ /* msg data is a notification */
+ return -EBADMSG;
+
+ ctl_len = (char *)msg.msg_control - (char *)&ctlbuf;
+ if (ctl_len >= ctlbuf.cmsg.cmsg_len
+ && ctlbuf.cmsg.cmsg_level == IPPROTO_SCTP
+ && ctlbuf.cmsg.cmsg_type == SCTP_SNDRCV) {
+ struct sctp_sndrcvinfo *sinfo = CMSG_DATA(&ctlbuf.cmsg);
+ ss7_trans_set_msg_info(ss7_msg, sinfo->sinfo_stream, sinfo->sinfo_ppid);
+ }
+
+ msg_len = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
+ if (msg_len >= 65556)
+ /* Disbelieve this is valid data */
+ return -EIO;
+
+ if (rval != msg_len || !(msg.msg_flags & MSG_EOR))
+ return -EIO;
+ return rval;
+}
+
+int
+ss7_trans_init_sctp_sinfo(void *buf, int maxlen, __u16 **stream, __u32 **ppid)
+{
+ struct cmsghdr *cmsg;
+ struct sctp_sndrcvinfo *sinfo;
+
+ if (maxlen < CMSG_LEN(sizeof *sinfo))
+ return -1;
+
+ cmsg = buf;
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof *sinfo);
+ sinfo = CMSG_DATA(cmsg);
+ memset(sinfo, 0, sizeof *sinfo);
+ *stream = &sinfo->sinfo_stream;
+ *ppid = &sinfo->sinfo_ppid;
+
+ return CMSG_LEN(sizeof *sinfo);
+}
\ No newline at end of file
diff --git a/a/2.hdr b/a/2.hdr
deleted file mode 100644
index 7ca41cd..0000000
--- a/a/2.hdr
+++ /dev/null
@@ -1,6 +0,0 @@
-Content-Type: text/plain; name=ss7osglue.c; charset=WINDOWS-1252
-Content-Description: ss7osglue.c
-Content-Disposition: attachment; filename="ss7osglue.c"; size=36047;
- creation-date="Thu, 26 Sep 2019 10:15:35 GMT";
- modification-date="Thu, 26 Sep 2019 10:15:35 GMT"
-Content-Transfer-Encoding: base64
diff --git a/a/2.txt b/a/2.txt
deleted file mode 100644
index c3e7b95..0000000
--- a/a/2.txt
+++ /dev/null
@@ -1,1381 +0,0 @@
-#ident "@(#) (c) Aculab plc $Header: /home/cvs/repository/ss7/stack/src/driver/linux/ss7osglue.c,v 1.157 2019-08-29 16:09:14 davidla Exp $ $Name: $"
-#ifndef MODULE
-#define MODULE
-#endif
-
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-#error minimum kernel version is 2.6.28
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
-#include <generated/autoconf.h>
-#else
-#include <linux/autoconf.h>
-#endif
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/kmod.h>
-#include <linux/string.h>
-#include <linux/sched.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
-#include <linux/sched/signal.h>
-#endif
-#include <linux/wait.h>
-#include <linux/socket.h>
-#include <linux/signal.h>
-#include <linux/poll.h>
-#include <linux/net.h>
-#include <linux/nsproxy.h>
-#include <linux/in.h>
-#include <linux/reboot.h>
-#include <asm/atomic.h>
-#include <asm/uaccess.h>
-
-#include <linux/kthread.h>
-
-/* This is only in the kernel build tree */
-#include <net/sock.h>
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
-#include <uapi/linux/sctp.h>
-#else
-#include <net/sctp/user.h> /* netinet/sctp.h ought to be this file */
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
-#define wait_queue_head __wait_queue_head
-#define wait_queue_entry __wait_queue
-#endif
-
-#define SK_PROTOCOL(sock) (sock)->sk->sk_protocol
-
-extern void ss7_trace_mem(int, void *, int, const char *, ...);
-extern void ss7_trace_printf(int, const char *, ...);
-
-/* Aculab DACP interfaces - these are in aculab's kern_if.h */
-void *dacp_symbol_get(const char *);
-int dacp_symbol_release(const char *);
-
-MODULE_AUTHOR("Aculab");
-MODULE_LICENSE("Proprietary");
-
-#include "ss7osglue.h"
-
-/* Mutex for driver interface code */
-static struct mutex ss7_glue_mutex;
-
-static int ss7dev_major;
-static const void *ss7_dtls_handle;
-static int ss7_use_count;
-static int ss7_stop_pid;
-
-static struct task_struct *asserted_tasks[16];
-static unsigned int asserted_task_count;
-
-typedef char ss7_verify_const[ SS7_SOCK_STREAM == SOCK_STREAM && SS7_SOCK_SEQPACKET == SOCK_SEQPACKET ? 1 : -1];
-
-static void ss7_net_ns_unload(void);
-
-#define TCP_NODELAY 1
-
-static int ss7_glue_open(struct inode *, struct file *);
-static int ss7_glue_release(struct inode *, struct file *);
-static long ss7_glue_unlocked_ioctl(struct file *, unsigned int, unsigned long);
-static unsigned int ss7_glue_poll(struct file *const, poll_table *);
-
-static struct file_operations ss7dev_fop =
-{
- open: ss7_glue_open,
- release: ss7_glue_release,
- unlocked_ioctl: ss7_glue_unlocked_ioctl,
- compat_ioctl: ss7_glue_unlocked_ioctl,
- poll: ss7_glue_poll,
- owner: THIS_MODULE
-};
-
-static int ss7_reboot_notify(struct notifier_block *nb, unsigned long action,
- void *data)
-{
- /* System being rebooted.
- * I added this hoping to use it to get the ss7maint daemon to exit,
- * but it isn't called until all user processes have died.
- * Leave it here - might be useful one day. */
- return 0;
-}
-
-static struct notifier_block ss7_reboot_notifier_block = {
- .notifier_call = ss7_reboot_notify,
-};
-
-static int
-ss7_init_fail(int rval)
-{
- if (ss7dev_major > 0)
- unregister_chrdev(ss7dev_major, "ss7server");
- return rval;
-}
-
-static int
-ss7_init_mod(void)
-{
- const void *(*dtls_register)(const char *, int (*)(struct dtls_get_if *));
- int rval;
-
- ss7_mutex_init(&ss7_glue_mutex);
-
- printk(KERN_INFO "%s\n", ss7version);
-
- ss7dev_major = register_chrdev(0, "ss7server", &ss7dev_fop);
-
- if (ss7dev_major < 0) {
- printk(KERN_INFO "ss7server: register_chrdev() failed: %d\n",
- ss7dev_major);
- return ss7_init_fail(ss7dev_major);
- }
-
- rval = ss7_driver_init();
- if (rval != 0) {
- printk(KERN_INFO "ss7server: ss7_driver_init() failed: %d\n", rval);
- return ss7_init_fail(-EIO);
- }
-
- dtls_register = dacp_symbol_get("acuc_dtls_register");
- if (dtls_register == NULL)
- printk(KERN_INFO "ss7server: cannot locate \"acuc_dtls_register\"\n");
- else
- ss7_dtls_handle = dtls_register(DYNAMIC_TLS_PREFIX "ss7",
- ss7_tls_get_if);
-
- register_reboot_notifier(&ss7_reboot_notifier_block);
- return 0;
-}
-
-static void
-ss7_cleanup_mod(void)
-{
- int (*dtls_unregister)(const void *);
-
- unregister_reboot_notifier(&ss7_reboot_notifier_block);
-
- if (ss7_dtls_handle != NULL) {
- dtls_unregister = dacp_symbol_get("acuc_dtls_unregister");
- dacp_symbol_release("acuc_dtls_register");
- if (dtls_unregister != NULL) {
- dtls_unregister(ss7_dtls_handle);
- dacp_symbol_release("acuc_dtls_unregister");
- }
- }
-
- ss7_init_fail(0);
-
- printk(KERN_INFO "Aculab ss7server: driver unloaded\n");
-}
-
-module_init(ss7_init_mod)
-module_exit(ss7_cleanup_mod)
-
-static int
-ss7_glue_open(struct inode *const inode, struct file *const filp)
-{
- int rval, pid;
-
- if (filp->private_data)
- /* Duplicate open */
- return 0;
-
- ss7_mutex_enter(&ss7_glue_mutex);
- if (ss7_use_count < 0) {
- /* ss7_driver_shutdown() has been called, to late to do anything */
- ss7_mutex_exit(&ss7_glue_mutex);
- return -EIO;
- }
- ss7_use_count++;
- ss7_mutex_exit(&ss7_glue_mutex);
-
- rval = ss7_devif_open(&filp->private_data);
- if (rval != 0) {
- ss7_mutex_enter(&ss7_glue_mutex);
- ss7_use_count--;
- ss7_mutex_exit(&ss7_glue_mutex);
- pid = ss7_pid();
- if (pid != ss7_stop_pid)
- printk(KERN_INFO "ss7_devif_open() pid %d failed ss7 error %d\n",
- pid, rval);
- return -EIO;
- }
-
- return 0;
-}
-
-static int
-ss7_glue_release(struct inode *const inode, struct file *const filp)
-{
- if (filp->private_data)
- ss7_devif_close(filp->private_data);
-
- ss7_mutex_enter(&ss7_glue_mutex);
- ss7_use_count--;
-
- if (ss7_use_count == 0 && ss7_stop_pid != 0) {
- /* Last user process has gone, complete shutdown functions */
- ss7_net_ns_unload();
- /* Stop any more opens */
- ss7_use_count = -1;
- ss7_driver_shutdown();
- }
-
- ss7_mutex_exit(&ss7_glue_mutex);
-
- return 0;
-}
-
-static long
-ss7_glue_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- if (!filp->private_data)
- return -ENODEV;
-
- switch (cmd) {
-
- case SS7_STOP: /* ss7maint shutting us down */
- /* Start shutdown now, will complete on last close */
- ss7_driver_stop();
- ss7_stop_pid = ss7_pid();
- return 0;
-
- /* Request from ss7maint or user application */
- case SS7_USER_IOCTL_CODE:
- return ss7dev_ioctl(filp->private_data, cmd, arg);
-
- default:
- return -ENOTTY;
- }
-}
-
-static unsigned int
-ss7_glue_poll(struct file *filp, poll_table *pt)
-{
- poll_wait(filp, *ss7_devif_get_pollqueue_head(filp->private_data), pt);
- return ss7_devif_get_poll_status(filp->private_data);
-}
-
-void *
-ss7_os_malloc(int s, int ss7_flags)
-{
- return kmalloc(s, GFP_KERNEL);
-}
-
-void
-ss7_os_free(void *p)
-{
- kfree(p);
-}
-
-void
-ss7_poll_queue_head_deinit(wait_queue_head_t **pqhp)
-{
- ss7_os_free(*pqhp);
-}
-
-int
-ss7_poll_queue_head_init(wait_queue_head_t **pqhp)
-{
- wait_queue_head_t *pqh = ss7_os_malloc(sizeof *pqh, 0);
- if (pqh == NULL)
- return -1;
- init_waitqueue_head(pqh);
- *pqhp = pqh;
- return 0;
-}
-
-void
-ss7_pollwakeup(wait_queue_head_t **pqh, unsigned int poll_event)
-{
- wake_up(*pqh);
-}
-
-void
-ss7_kill_task(struct task_struct *task, int signo)
-{
- /* Send signal even though set to SIG_IGN */
- force_sig(signo, task);
-}
-
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
-/* spinlock_t is a typedef for an unnamed structure so we can't
- * make 'struct spinlock' match the kernel spinlock type. */
-#define SPINLOCK_CAST (spinlock_t *)
-#else
-#define SPINLOCK_CAST
-#endif
-
-size_t
-ss7_spin_lock_size(void)
-{
- return sizeof *SPINLOCK_CAST(struct spinlock *)0;
-}
-
-void
-ss7_spin_lock_init(struct spinlock *s)
-{
- spin_lock_init(SPINLOCK_CAST s);
-}
-
-void
-ss7_spin_lock_enter(struct spinlock *s)
-{
- spin_lock(SPINLOCK_CAST s);
-}
-
-void
-ss7_spin_lock_exit(struct spinlock *s)
-{
- spin_unlock(SPINLOCK_CAST s);
-}
-
-size_t
-ss7_mutex_size(void)
-{
- return sizeof(struct mutex);
-}
-
-void
-ss7_mutex_init(struct mutex *s)
-{
- mutex_init(s);
-}
-
-void
-ss7_mutex_enter(struct mutex *s)
-{
- mutex_lock(s);
-}
-
-int
-ss7_mutex_enter_tmo(struct mutex *s, int max_wait)
-{
- /* There is no mutex_enter_timeout() however this was all added
- * to stop status commands sleeping forever when a process has
- * 'oopsed' with a mutex held.
- * Do a sneak check on the state of any owning task then
- * wait interruptibly.
- * ^C should error out the status call. */
-
- /* If uncontended just acquire */
- if (mutex_trylock(s))
- return 1;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
- {
- struct task_struct *owner;
- int state;
-
- spin_lock(&s->wait_lock);
- owner = __mutex_owner(s);
- state = owner ? owner->state : 0;
- spin_unlock(&s->wait_lock);
- if (state & TASK_DEAD)
- /* mutex will never be released, treat as timeout */
- return 0;
- }
-#endif
-
- /* If C7_ASSERT() has been called, just let everyone in */
- if (asserted_task_count)
- return 0;
-
- return mutex_lock_interruptible(s) ? -1 /* EINTR */ : 1 /* acquired */;
-}
-
-void
-ss7_mutex_exit(struct mutex *s)
-{
- mutex_unlock(s);
-}
-
-size_t
-ss7_cv_size(void)
-{
- return sizeof(wait_queue_head_t);
-}
-
-void
-ss7_cv_init(wait_queue_head_t *const v)
-{
- init_waitqueue_head(v);
-}
-
-static int
-ss7_schedule_tmo(int tmo_ms)
-{
- int tmo_jiffies;
-
- /* Really sleep - unless woken since unlocking spinlock */
- if (tmo_ms >= 0) {
- if (tmo_ms <= 1)
- tmo_jiffies = tmo_ms;
- else
- /* Convert to jiffies and round up */
- tmo_jiffies = 1 + (tmo_ms + 1 - 1) * 16 / (16000/HZ);
- /* Return value of schedule_timeout() is unexpired timeout */
- /* We want 0 for 'timedout' (to match cv_wait_sig()) */
- return schedule_timeout(tmo_jiffies) != 0;
- }
-
- schedule();
- if (!signal_pending(current))
- /* Woken by the event */
- return 1;
-
- /* Report 0 for a signal, except -1 for SIGKILL (reboot) */
- return sigismember(¤t->pending.signal, SIGKILL) ? -1 : 0;
-}
-
-int
-ss7_cv_wait_guts(wait_queue_head_t *cvp, struct mutex *mtxp,
- int interruptible, int tmo_ms)
-{
- int r;
- struct wait_queue_entry w;
- int sleep_state;
-
- init_waitqueue_entry(&w, current);
-
- /* Tell scheduler we are going to sleep... */
- if (signal_pending(current) && !interruptible)
- /* We don't want waking immediately (again) */
- sleep_state = TASK_UNINTERRUPTIBLE;
- else
- sleep_state = TASK_INTERRUPTIBLE;
- set_current_state(sleep_state);
-
- /* Connect to condition variable ... */
- add_wait_queue(cvp, &w);
- mutex_unlock(mtxp); /* Release mutex */
-
- r = ss7_schedule_tmo(tmo_ms);
-
- /* Disconnect from condition variable ... */
- remove_wait_queue(cvp, &w);
-
- /* Re-acquire mutex */
- mutex_lock(mtxp);
-
- /* return 1 if woken, 0 if timed_out/signal, -1 if SIGKILL */
- return r;
-}
-
-int
-ss7_cv_wait_spin_lock(wait_queue_head_t *cvp, struct spinlock *lock,
- int interruptible, int tmo_ms)
-{
- int r;
- struct wait_queue_entry w;
- int sleep_state;
-
- init_waitqueue_entry(&w, current);
-
- /* Tell scheduler we are going to sleep... */
- if (signal_pending(current) && !interruptible)
- /* We don't want waking immediately (again) */
- sleep_state = TASK_UNINTERRUPTIBLE;
- else
- sleep_state = TASK_INTERRUPTIBLE;
- set_current_state(sleep_state);
-
- /* Connect to condition variable ... */
- add_wait_queue(cvp, &w);
- spin_unlock(SPINLOCK_CAST lock);
-
- r = ss7_schedule_tmo(tmo_ms);
-
- /* Disconnect from condition variable ... */
- remove_wait_queue(cvp, &w);
-
- /* Re-acquire mutex */
- spin_lock(SPINLOCK_CAST lock);
-
- return r;
-}
-
-/*---------------------------------------------------------------------**
-** ss7_cv_broadcast **
-** Awaken all threads that are sleeping on a condition variable. **
-** Caller must use the associated mutex sensibly, i.e. ... **
-** acquire the mutex **
-** Set some flag that a sleeping thread will check for **
-** ss7_cv_broadcast() **
-** release the mutex **
-**---------------------------------------------------------------------*/
-
-void
-ss7_cv_broadcast(wait_queue_head_t *const cvp)
-{
- wake_up(cvp);
-}
-
-
-unsigned long
-ss7_copy_to_user(void *to, const void *from, unsigned long c)
-{
- return copy_to_user(to, from, c);
-}
-
-unsigned long
-ss7_copy_from_user(void *to, const void *from, unsigned long c)
-{
- return copy_from_user(to, from, c);
-}
-
-unsigned int
-ss7_pid(void)
-{
- return current->pid;
-}
-
-struct task_struct *
-ss7_current_task(void)
-{
- return current;
-}
-
-unsigned int
-ss7_task_pid(struct task_struct *task)
-{
- return task->pid;
-}
-
-int
-ss7_glue_thread_fn(void *ss7_thread)
-{
- ss7_thread_run(ss7_thread);
- module_put_and_exit(0);
- return 0;
-}
-
-struct task_struct *
-ss7_os_thread_create(struct ss7_thread *thrp, const char *desc)
-{
- struct task_struct *task;
- const char *sp;
- int len;
-
- if (!try_module_get(THIS_MODULE))
- return NULL;
-
- /* The thread description gets truncated to 15 chars, can't be helped!
- * Use 'ss7maint osstatus -t' to get the full description. */
-
- /* Remove any leading space and truncate after second word */
- if (desc[0] == ' ')
- desc++;
- len = 100;
- sp = ss7strchr(desc, ' ');
- if (sp != NULL) {
- sp = ss7strchr(sp + 1, ' ');
- if (sp != NULL)
- len = sp - desc;
- }
-
- task = kthread_run(ss7_glue_thread_fn, thrp, "ss7:%.*s", len, desc);
- if (IS_ERR(task)) {
- module_put(THIS_MODULE);
- return NULL;
- }
- return task;
-}
-
-void
-ss7_ms_delay(const unsigned int ms)
-{
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((unsigned long long)HZ * ms / 1000);
-}
-
-int
-ss7_os_get_ticks(void)
-{
- return jiffies;
-}
-
-int
-ss7_os_ticks_to_us(int interval)
-{
- return interval * 1000000 / HZ;
-}
-
-int
-ss7_os_ticks_to_ms(int interval)
-{
- return interval * 1000 / HZ;
-}
-
-int
-ss7_os_ticks_to_secs(int interval)
-{
- return interval / HZ;
-}
-
-unsigned int
-ss7_get_ms_time(void)
-{
- static unsigned long epoch;
- struct timespec now;
-
- getrawmonotonic(&now);
-
- if (epoch == 0)
- epoch = now.tv_sec;
-
- return (now.tv_sec - epoch) * 1000 + now.tv_nsec / 1000000;
-}
-
-struct acu_ss7maint_time {
- unsigned int st_sec;
- unsigned int st_usec;
-};
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
-static inline void do_gettimeofday(struct timeval *tv)
-{
- struct timespec64 ts;
-
- ktime_get_real_ts64(&ts);
- tv->tv_sec = ts.tv_sec;
- tv->tv_usec = ts.tv_nsec/1000u;
-}
-#endif
-
-void
-ss7_get_timestamp(struct acu_ss7maint_time *ptime)
-{
- struct timeval tv;
-
- /* do_gettimeofday() returns 'wall clock time'.
- * It can go backwards. */
- do_gettimeofday(&tv);
- ptime->st_sec = tv.tv_sec;
- ptime->st_usec = tv.tv_usec;
-}
-
-unsigned int
-ss7_get_elapsed(const struct acu_ss7maint_time *epoch)
-{
- struct timeval tv;
- do_gettimeofday(&tv);
-
- return tv.tv_sec - epoch->st_sec;
-}
-
-void
-ss7_os_log_error(const char *text)
-{
- printk(KERN_EMERG "ss7server: %s", text);
- if (memcmp(text, "Assertion fail", 14) == 0) {
- dump_stack();
- /* Although we return, the caller sleeps forever */
- /* Remember the 'stuck' tasks */
- asserted_tasks[asserted_task_count++ & 15] = current;
- }
-}
-
-/*---------------------------------------------------------------------**
-** Miscellanous string and memory functions **
-**---------------------------------------------------------------------*/
-
-void
-ss7memzero(void *buf, size_t len)
-{
- memset(buf, 0, len);
-}
-
-void
-ss7memcpy(void *dest, const void *src, size_t len)
-{
- memcpy(dest, src, len);
-}
-
-void
-ss7_memmove(void *dest, const void *src, size_t len)
-{
- memmove(dest, src, len);
-}
-
-int
-ss7memcmp(const void *s1, const void *s2, size_t len)
-{
- return memcmp(s1, s2, len);
-}
-
-unsigned int
-ss7strlen(const char *str)
-{
- return strlen(str);
-}
-
-void
-ss7strcpy(char *dest, const char *src)
-{
- strcpy(dest, src);
-}
-
-int
-ss7strcmp(const char *dest, const char *src)
-{
- return strcmp(dest, src);
-}
-
-char *
-ss7strncpy(char *const s1, const char *s2, size_t n)
-{
- return strncpy(s1, s2, n);
-}
-
-char *
-ss7strchr(const char *s, const int c)
-{
- return strchr(s, c);
-}
-
-/*---------------------------------------------------------------------**
-** TCP/IP functions **
-**---------------------------------------------------------------------*/
-
-int
-ss7_sctp_supported(void)
-{
- return 1;
-}
-
-unsigned int
-ss7_get_default_af_opts(unsigned int protocol, unsigned int port)
-{
- /* The SS7 driver needs to know the which address families (IPv4 or IPv6)
- * to use for listening sockets.
- *
- * Whether an IPV6 socket can accept IPV4 connections depends on
- * the IPV6_V6ONLY socket option. The default for which depends
- * on net.ipv6.bindv6only (which usually defaults to 0 - allowing IPV4).
- * There also might be kernels where clearing IPV6_V6ONLY is disallowed.
- *
- * Normally only a single socket is created for each port since an IPv6
- * socket can receive IPv4 connections. However a separate IPv4 socket
- * can be requested.
- *
- * This function should return one of:
- * SS7_AF_OPT_IPv6
- * IPV6 socket with the default IPV6_V6ONLY value.
- * SS7_AF_OPT_IPv6_V6ONLY_CLR
- * IPV6 socket with IPV6_V6ONLY explicitly cleared.
- * SS7_AF_OPT_IPv6_V6ONLY_SET
- * IPV6 socket with IPV6_V6ONLY explicitly set.
- * Possibly logically ored with:
- * SS7_AF_OPT_IPv4
- * A separate IPv4 socket.
- *
- * For flexibility the decision can be based on the protocol (either
- * IPPROTO_SCTP or IPPROTO_TCP) or the port number.
- *
- * Default to creating a single socket and disabling IPV6_V6ONLY.
- */
-#ifndef SS7_DEFAULT_AF_OPTS
-#define SS7_DEFAULT_AF_OPTS SS7_AF_OPT_IPv6
-#endif
- return SS7_DEFAULT_AF_OPTS;
-}
-
-/* kernel_get/set_sockopt() prototypes have (char *) for the buffer.
- * #define a (void *) cast.
- */
-#define kernel_setsockopt(sock, level, name, val, len) \
- kernel_setsockopt(sock, level, name, (void *)val, len)
-#define kernel_getsockopt(sock, level, name, val, len) \
- kernel_getsockopt(sock, level, name, (void *)val, len)
-
-/* Note that we can't (easily) hold reference counts on the namespace
- * because put_net() is GPL_ONLY.
- * Instead we keep our own table and create a socket to hold the
- * reference for us.
- * Table entries 0 and 1 always refer to init_net and the namespace
- * of the (last started) ss7 daemon. Neither is reference counted
- * (although we hold a single reference on the latter).
- * Higher entries are saved from invocations of 'ss7maint start'
- * and 'firmware download'. */
-
-static struct ss7_ns_info {
- struct net *ni_net_ns;
- struct socket *ni_sock;
- unsigned int ni_refcount;
-} ss7_ns_table[256];
-
-static struct socket *
-ss7_glue_create_ns_socket(struct net *net)
-{
- struct socket *sock;
-
- if (__sock_create(net, AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock, 0))
- return NULL;
- return sock;
-}
-
-void
-ss7_net_ns_get(unsigned int namespace)
-{
- unsigned int idx = SS7_NET_NS_IDX(namespace);
-
- if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
- /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
- return;
-
- ss7_mutex_enter(&ss7_glue_mutex);
- ss7_ns_table[idx].ni_refcount++;
- ss7_mutex_exit(&ss7_glue_mutex);
-
- ss7_trace_printf(0, "ss7_net_ns_get(%x): refcount %d, sock %p, net %p\n",
- namespace, ss7_ns_table[idx].ni_refcount, ss7_ns_table[idx].ni_sock,
- ss7_ns_table[idx].ni_net_ns);
-}
-
-void
-ss7_net_ns_put(unsigned int namespace)
-{
- struct ss7_ns_info *ni;
- unsigned int idx = SS7_NET_NS_IDX(namespace);
-
- if (idx <= SS7_NET_NS_IDX(SS7_NET_NS_DAEMON))
- /* SS7_NET_NS_INIT and SS7_NET_NS_DAEMON aren't ref-counted */
- return;
- ni = ss7_ns_table + idx;
-
- ss7_trace_printf(0, "ss7_net_ns_put(%x): refcount %d, sock %p, net %p\n",
- namespace, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
-
- ss7_mutex_enter(&ss7_glue_mutex);
- if (ni->ni_refcount && !--ni->ni_refcount) {
- /* Last reference gone */
- sock_release(ni->ni_sock);
- ni->ni_net_ns = NULL;
- ni->ni_sock = NULL;
- }
- ss7_mutex_exit(&ss7_glue_mutex);
-}
-
-static void
-ss7_net_ns_unload(void)
-{
- unsigned int idx;
- struct ss7_ns_info *ni;
-
- for (idx = 1; idx < ARRAY_SIZE(ss7_ns_table); idx++) {
- ni = ss7_ns_table + idx;
- if (!ni->ni_sock)
- continue;
-
- /* This should only report anything for the 'daemon' slot */
- printk(KERN_INFO "ss7_net_ns_unload(): idx %d, refcount %d, sock %p, net %p\n",
- idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
- sock_release(ni->ni_sock);
- ni->ni_net_ns = NULL;
- ni->ni_sock = NULL;
- ni->ni_refcount = 0;
- }
-}
-
-unsigned int
-ss7_net_ns_set(unsigned int new_namespace, unsigned int old_namespace)
-{
- static unsigned int num_used_idx = 2;
- unsigned int idx, free_idx;
- struct ss7_ns_info *ni;
- struct net *net;
-
- /* The new_namespace should have the low 16 bits zero.
- * The low bits of old_namespace indicate what was actually being used. */
-
- if (new_namespace != SS7_NET_NS_START) {
- ss7_net_ns_put(old_namespace);
- return new_namespace == SS7_NET_NS_DAEMON ? SS7_NET_NS_DAEMON : SS7_NET_NS_INIT;
- }
-
- /* SS7_NET_NS_START - look for an entry for the namespace of the current
- * process (which will be 'ss7maint start'). */
- net = current->nsproxy->net_ns;
-
- idx = SS7_NET_NS_IDX(old_namespace);
- ni = ss7_ns_table + idx;
- if (ni->ni_net_ns == net)
- /* Unchanged index, no need to change reference count */
- return SS7_NET_NS_START | idx;
-
- /* Different slot needed, drop old reference */
- ss7_net_ns_put(old_namespace);
-
- /* Check init and daemon entries, neither goes away */
- if (idx != SS7_NET_NS_IDX(SS7_NET_NS_INIT)
- && net == &init_net)
- return SS7_NET_NS_START | SS7_NET_NS_IDX(SS7_NET_NS_INIT);
-
- idx = SS7_NET_NS_IDX(SS7_NET_NS_DAEMON);
- ni = ss7_ns_table + idx;
- if (net == ni->ni_net_ns)
- return SS7_NET_NS_START | idx;
-
- ss7_mutex_enter(&ss7_glue_mutex);
-
- /* Scan table for an existing reference */
- free_idx = 0;
- for (idx = 2; idx < num_used_idx; idx++) {
- ni = ss7_ns_table + idx;
- if (ni->ni_net_ns == net) {
- /* found a match */
- ni->ni_refcount++;
- ss7_mutex_exit(&ss7_glue_mutex);
- ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): found idx %d, refcount %d, sock %p, net %p\n",
- new_namespace, old_namespace, idx, ni->ni_refcount, ni->ni_sock, ni->ni_net_ns);
- return SS7_NET_NS_START | idx;
- }
- if (!free_idx && !ni->ni_net_ns)
- free_idx = idx;
- }
-
- /* Not found allocate lowest free slot */
- if (!free_idx) {
- if (num_used_idx >= ARRAY_SIZE(ss7_ns_table))
- /* Table full, borked */
- goto no_ref;
- free_idx = num_used_idx++;
- }
-
- ni = &ss7_ns_table[free_idx];
- ni->ni_sock = ss7_glue_create_ns_socket(net);
- if (!ni->ni_sock)
- goto no_ref;
- ni->ni_net_ns = net;
-
- ss7_mutex_exit(&ss7_glue_mutex);
- ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): new idx %d, sock %p, net %p\n",
- new_namespace, old_namespace, free_idx, ni->ni_sock, ni->ni_net_ns);
-
- return SS7_NET_NS_START | free_idx;
-
- no_ref:
- ss7_mutex_exit(&ss7_glue_mutex);
- ss7_trace_printf(0, "ss7_net_ns_set(%x, %x): no_ref\n",
- new_namespace, old_namespace);
- return SS7_NET_NS_START;
-}
-
-void
-ss7_glue_daemon_open(void)
-{
- struct ss7_ns_info *ni = &ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_DAEMON)];
- struct net *net = current->nsproxy->net_ns;
-
- /* Save (and reference count) the network namespace the ss7 daemon
- * is started in. */
-
- /* Initialise the entry for init_net here - has to be done somewhere. */
- ss7_ns_table[SS7_NET_NS_IDX(SS7_NET_NS_INIT)].ni_net_ns = &init_net;
-
- if (net == ni->ni_net_ns)
- /* Unchanged */
- return;
-
- if (ni->ni_sock)
- sock_release(ni->ni_sock);
- ni->ni_sock = NULL;
-
- if (net != &init_net && !((ni->ni_sock = ss7_glue_create_ns_socket(net))))
- /* Can't create socket, default to global namespace */
- net = &init_net;
-
- ni->ni_net_ns = net;
-}
-
-int
-ss7_socket(int family, int type, int protocol, unsigned int namespace, struct socket **sockp)
-{
- struct socket *sock;
- struct net *net;
- unsigned int one = 1U;
- int rval;
-
- net = ss7_ns_table[SS7_NET_NS_IDX(namespace)].ni_net_ns;
- if (!net)
- net = &init_net;
-
- /* If we have to autoload the sctp module, we might re-enter it
- * before it has finished initialising - might go 'boom'. */
- ss7_mutex_enter(&ss7_glue_mutex);
-
- /* sock_create_kern() creates a socket that doesn't hold a reference
- * to the namespace (they get used for sockets needed by the protocol
- * stack code itself).
- * We need a socket that holds a reference to the namespace, so create
- * a 'user' socket in a specific namespace.
- * This adds an extra security check which we should pass because all the
- * sockets are created by kernel threads.
- */
- rval = __sock_create(net, family, type, protocol, sockp, 0);
- ss7_mutex_exit(&ss7_glue_mutex);
- if (rval != 0)
- return rval;
- sock = *sockp;
-
- kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
-
- return 0;
-}
-
-void
-ss7_setsockopt_nodelay(struct socket *sock, int enabled)
-{
- kernel_setsockopt(sock, SK_PROTOCOL(sock),
- SK_PROTOCOL(sock) == IPPROTO_TCP ? TCP_NODELAY : SCTP_NODELAY,
- &enabled, sizeof enabled);
-}
-
-static void
-ss7_sctp_set_opts(struct socket *sock)
-{
- struct sctp_event_subscribe events;
- int len, rval;
-
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return;
-
- len = sizeof events;
- rval = kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, &len);
- if (rval != 0)
- return;
-
- /* We need to know the stream and ppid */
- events.sctp_data_io_event = 1;
- /* Enable notifications to detect connection restart */
- events.sctp_association_event = 1;
- kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof events);
-}
-
-unsigned int
-ss7_get_max_sctp_ostreams(struct socket *sock)
-{
- struct sctp_status sstat;
- int len;
-
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return 0;
-
- len = sizeof sstat;
- if (kernel_getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &sstat, &len))
- return 0;
-
- return sstat.sstat_outstrms;
-}
-
-void
-ss7_set_max_sctp_streams(struct socket *sock, unsigned int max_streams)
-{
- struct sctp_initmsg sinit;
-
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return;
-
- memset(&sinit, 0, sizeof sinit);
-
- sinit.sinit_num_ostreams = max_streams;
- sinit.sinit_max_instreams = max_streams;
- kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_INITMSG, &sinit, sizeof sinit);
-}
-
-void
-ss7_trans_setsockopt(struct socket *sock)
-{
- unsigned int one = 1U;
-
- ss7_setsockopt_nodelay(sock, 1);
- ss7_sctp_set_opts(sock);
- if (SK_PROTOCOL(sock) == IPPROTO_TCP)
- kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
-}
-
-void
-ss7_transbind_setsockopt(struct socket *sock)
-{
- /* Set options for a listening socket */
- ss7_sctp_set_opts(sock);
-
- /* M3UA may need 16 data streams, it is just TFH to configure this */
- ss7_set_max_sctp_streams(sock, 1 + 16);
-}
-
-#define IP_ADDR_LEN(sa) ((sa)->sin6_family == AF_INET6 ? sizeof *(sa) : 16)
-int
-ss7_connect(struct socket *sock, struct sockaddr_in6 *sa)
-{
- return kernel_connect(sock, (void *)sa, IP_ADDR_LEN(sa), O_RDWR);
-}
-
-int
-ss7_bind(struct socket *sock, struct sockaddr_in6 *sa, unsigned int af_opts)
-{
- /* If we are binding INADDR6_ANY to an IPv6 socket (typically for
- * a listening socket) then we probably want to ensure that IPV6_V6ONLY
- * is 0 so that the socket will also be given IPv4 connections. */
- if (sa->sin6_family == AF_INET6 && af_opts & SS7_AF_OPT_IPv6_V6ONLY
- && sa->sin6_addr.in6_u.u6_addr32[0] == 0
- && (sa->sin6_addr.in6_u.u6_addr32[1]
- | sa->sin6_addr.in6_u.u6_addr32[2]
- | sa->sin6_addr.in6_u.u6_addr32[3]) == 0) {
- int v6only = af_opts & 1;
- kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof v6only);
- }
-
- return kernel_bind(sock, (void *)sa, IP_ADDR_LEN(sa));
-}
-
-int
-ss7_bindx(struct socket *sock, struct sockaddr_in6 *sa)
-{
- if (SK_PROTOCOL(sock) != IPPROTO_SCTP)
- return -EPROTONOSUPPORT;
-
- return kernel_setsockopt(sock, IPPROTO_SCTP, SCTP_SOCKOPT_BINDX_ADD,
- sa, IP_ADDR_LEN(sa));
-}
-
-int
-ss7_listen(struct socket *sock, int len)
-{
- return kernel_listen(sock, len);
-}
-
-int
-ss7_accept(struct socket *sock, struct socket **new_sockp, int flags)
-{
- return kernel_accept(sock, new_sockp, flags);
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
-static inline int
-ss7_kernel_getsockname(struct socket *sock, struct sockaddr *address)
-{
- int err, len;
-
- err = kernel_getsockname(sock, (struct sockaddr *)address, &len);
- return err ? err : len;
-}
-#define kernel_getsockname ss7_kernel_getsockname
-
-static inline int
-ss7_kernel_getpeername(struct socket *sock, struct sockaddr *address)
-{
- int err, len;
-
- err = kernel_getpeername(sock, (struct sockaddr *)address, &len);
- return err ? err : len;
-}
-#define kernel_getpeername ss7_kernel_getpeername
-#endif
-
-int
-ss7_get_loc_port(struct socket *sock)
-{
- char address[128 /*MAX_SOCK_ADDR*/];
- int len;
-
- len = kernel_getsockname(sock, (struct sockaddr *)address);
- if (len < 0)
- return 0;
-
- /* This works well enough for IPv4 and IPv6 */
- return ntohs(((struct sockaddr_in *)address)->sin_port);
-}
-
-int
-ss7_get_rem_addr(struct socket *sock, struct sockaddr_in6 *saddr)
-{
- int len;
-
- len = kernel_getpeername(sock, (struct sockaddr *)saddr);
- if (len < 0)
- return len;
-
- if (len > sizeof *saddr)
- printk(KERN_EMERG "ss7server: socket address (family %d) %d > %d",
- saddr->sin6_family, len, (int)sizeof *saddr);
-
- return 0;
-}
-
-int
-ss7_shutdown(struct socket *sock, int how)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
- if (SK_PROTOCOL(sock) == IPPROTO_SCTP) {
- struct linger linger;
-
- /* If we call kernel_sock_shutdown() then the connection isn't released
- * until all outstanding data has been acked.
- * If the remote system sends an INIT (restarting the connection)
- * while the linux kernel is waiting for data to be acked then it
- * will never disconnect.
- * Enabling 'linger' with a delay of zero causes sock_release()
- * to abort the connection (sends an ABORT chunk).
- *
- * The ss7 code never needs to wait for sent data to be acked,
- * so aborting the connection doesn't really matter.
- * All calls to ss7_shutdown() are immediately followed by calls to
- * ss7_closesocket().
- *
- * Plausibly we should always abort connections if we are disconnecting
- * due to an application level timeout.
- *
- * Fixed by the kernel patch:
- * "sctp: handle association restarts when the socket is closed"
- * Known to be included in the following kernels:
- * - mainline 3.18
- * - Ubuntu 3.13.11.11
- * Queued for 3.10-stable, 3.14-stable, 3.16-stable and 3.17-stable
- */
-
- linger.l_onoff = 1;
- linger.l_linger = 0;
- kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof linger);
-
- return 0;
- }
-#endif
- return kernel_sock_shutdown(sock, how);
-}
-
-void
-ss7_closesocket(struct socket *sock)
-{
- sock_release(sock);
-}
-
-int
-ss7_send(struct socket *sock, struct ss7_iovec *iov, int iovlen, int totlen,
- void *ctl, int ctl_len, unsigned int flags)
-{
- struct msghdr msg;
-
- msg.msg_name = 0;
- msg.msg_namelen = 0;
- msg.msg_control = ctl;
- msg.msg_controllen = ctl_len;
- msg.msg_flags = flags | MSG_NOSIGNAL;
-
- return kernel_sendmsg(sock, &msg, iov, iovlen, totlen);
-}
-
-int
-ss7_recv(struct socket *sock, unsigned char *data, int length, int flags)
-{
- struct kvec iov;
- struct msghdr msg;
-
- if (!sock->sk)
- return 0;
-
- iov.iov_len = length;
- iov.iov_base = data;
-
- msg.msg_name = 0;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
-
- return kernel_recvmsg(sock, &msg, &iov, 1, length, 0);
-}
-
-int
-ss7_recv_sctp(struct socket *sock, void *buf_1, int len_1, void *buf_2,
- int len_2, struct ss7_msgb *ss7_msg)
-{
- struct msghdr msg;
- struct kvec iov[2];
- unsigned char *data = buf_1;
- int msg_len, ctl_len;
- int rval;
- union {
- struct cmsghdr cmsg;
- unsigned int buf[16];
- } ctlbuf;
-
- if (!sock->sk)
- return 0;
-
- /* For SCTP each recvmsg should give us a single data record.
- * Since we only ever send SIGTRAN encoded messages bytes 4-7 are the
- * length - and should match that of the sctp data chunk.
- * buf_1/len_1 refer to the normal ss7 message buffer area, buf_2/len_2
- * are per-socket. Long messages get copied together by the caller.
- * The result is always a single valid SIGTRAN message */
-
- iov[0].iov_base = buf_1;
- iov[0].iov_len = len_1;
- iov[1].iov_base = buf_2;
- iov[1].iov_len = len_2;
-
- msg.msg_name = 0;
- msg.msg_namelen = 0;
- msg.msg_control = &ctlbuf;
- msg.msg_controllen = sizeof ctlbuf;
- msg.msg_flags = 0;
-
- rval = kernel_recvmsg(sock, &msg, iov, 2, len_1 + len_2, 0);
-
- if (rval <= 0)
- /* Don't return EBADMSG here */
- return rval != -EBADMSG ? rval : -EIO;
-
- if (msg.msg_flags & MSG_NOTIFICATION)
- /* msg data is a notification */
- return -EBADMSG;
-
- ctl_len = (char *)msg.msg_control - (char *)&ctlbuf;
- if (ctl_len >= ctlbuf.cmsg.cmsg_len
- && ctlbuf.cmsg.cmsg_level == IPPROTO_SCTP
- && ctlbuf.cmsg.cmsg_type == SCTP_SNDRCV) {
- struct sctp_sndrcvinfo *sinfo = CMSG_DATA(&ctlbuf.cmsg);
- ss7_trans_set_msg_info(ss7_msg, sinfo->sinfo_stream, sinfo->sinfo_ppid);
- }
-
- msg_len = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
- if (msg_len >= 65556)
- /* Disbelieve this is valid data */
- return -EIO;
-
- if (rval != msg_len || !(msg.msg_flags & MSG_EOR))
- return -EIO;
- return rval;
-}
-
-int
-ss7_trans_init_sctp_sinfo(void *buf, int maxlen, __u16 **stream, __u32 **ppid)
-{
- struct cmsghdr *cmsg;
- struct sctp_sndrcvinfo *sinfo;
-
- if (maxlen < CMSG_LEN(sizeof *sinfo))
- return -1;
-
- cmsg = buf;
- cmsg->cmsg_level = IPPROTO_SCTP;
- cmsg->cmsg_type = SCTP_SNDRCV;
- cmsg->cmsg_len = CMSG_LEN(sizeof *sinfo);
- sinfo = CMSG_DATA(cmsg);
- memset(sinfo, 0, sizeof *sinfo);
- *stream = &sinfo->sinfo_stream;
- *ppid = &sinfo->sinfo_ppid;
-
- return CMSG_LEN(sizeof *sinfo);
-}
\ No newline at end of file
diff --git a/a/content_digest b/N4/content_digest
index 444140f..981ae3e 100644
--- a/a/content_digest
+++ b/N4/content_digest
@@ -23,45 +23,16 @@
"From\0David Laight <David.Laight\@aculab.com>\0"
]
[
- "Subject\0RE: [Ocfs2-devel] [PATCH 27/33] sctp: export sctp_setsockopt_bindx\0"
+ "Subject\0[Cluster-devel] [Ocfs2-devel] [PATCH 27/33] sctp: export sctp_setsockopt_bindx\0"
]
[
"Date\0Sun, 17 May 2020 08:48:02 +0000\0"
]
[
- "To\0'Matthew Wilcox' <willy\@infradead.org>\0"
+ "To\0cluster-devel.redhat.com\0"
]
[
- "Cc\0'David Howells' <dhowells\@redhat.com>",
- " Christoph Hellwig <hch\@lst.de>",
- " Marcelo Ricardo Leitner <marcelo.leitner\@gmail.com>",
- " linux-nvme\@lists.infradead.org <linux-nvme\@lists.infradead.org>",
- " linux-kernel\@vger.kernel.org <linux-kernel\@vger.kernel.org>",
- " linux-sctp\@vger.kernel.org <linux-sctp\@vger.kernel.org>",
- " target-devel\@vger.kernel.org <target-devel\@vger.kernel.org>",
- " linux-afs\@lists.infradead.org <linux-afs\@lists.infradead.org>",
- " drbd-dev\@lists.linbit.com <drbd-dev\@lists.linbit.com>",
- " linux-cifs\@vger.kernel.org <linux-cifs\@vger.kernel.org>",
- " rds-devel\@oss.oracle.com <rds-devel\@oss.oracle.com>",
- " linux-rdma\@vger.kernel.org <linux-rdma\@vger.kernel.org>",
- " cluster-devel\@redhat.com <cluster-devel\@redhat.com>",
- " Jakub Kicinski <kuba\@kernel.org>",
- " linux-block\@vger.kernel.org <linux-block\@vger.kernel.org>",
- " Alexey Kuznetsov <kuznet\@ms2.inr.ac.ru>",
- " ceph-devel\@vger.kernel.org <ceph-devel\@vger.kernel.org>",
- " linux-nfs\@vger.kernel.org <linux-nfs\@vger.kernel.org>",
- " Neil Horman <nhorman\@tuxdriver.com>",
- " Hideaki YOSHIFUJI <yoshfuji\@linux-ipv6.org>",
- " netdev\@vger.kernel.org <netdev\@vger.kernel.org>",
- " Vlad Yasevich <vyasevich\@gmail.com>",
- " Eric Dumazet <edumazet\@google.com>",
- " Jon Maloy <jmaloy\@redhat.com>",
- " Ying Xue <ying.xue\@windriver.com>",
- " David S. Miller <davem\@davemloft.net>",
- " ocfs2-devel\@oss.oracle.com <ocfs2-devel\@oss.oracle.com>\0"
-]
-[
- "\0001:1\0"
+ "\0000:1\0"
]
[
"b\0"
@@ -110,21 +81,8 @@
"\n",
"-\n",
"Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK\n",
- "Registration No: 1397386 (Wales)"
-]
-[
- "\0001:2\0"
-]
-[
- "fn\0ss7osglue.c\0"
-]
-[
- "d\0ss7osglue.c\0"
-]
-[
- "b\0"
-]
-[
+ "Registration No: 1397386 (Wales)\n",
+ "-------------- next part --------------\n",
"#ident \"\@(#) (c) Aculab plc \$Header: /home/cvs/repository/ss7/stack/src/driver/linux/ss7osglue.c,v 1.157 2019-08-29 16:09:14 davidla Exp \$ \$Name: \$\"\n",
"#ifndef MODULE\n",
"#define MODULE\n",
@@ -1508,4 +1466,4 @@
"}"
]
-049f0d794ae7b86fe86338ebd5f3cc0175a0cd041f88b4d1c5c988d6b0c24e31
+fdf168d9eaa457044c84bd9c68fab91c6f954faf0f28653b3111bd91c339b388
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.