diff --git a/core/clist.c b/core/clist.c
new file mode 100644
index 0000000000000000000000000000000000000000..1e7db162699207272fa8c3de6658187dbbdc6380
--- /dev/null
+++ b/core/clist.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ *
+ * The code of _clist_sort() has been imported from
+ * https://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html.
+ * Original copyright notice:
+ *
+ * This file is copyright 2001 Simon Tatham.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/**
+ * @ingroup     core_util
+ * @{
+ *
+ * @file
+ * @brief       clist helper implementations
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * @}
+ */
+
+#include "clist.h"
+
+clist_node_t *_clist_sort(clist_node_t *list, clist_cmp_func_t cmp)
+{
+    clist_node_t *p, *q, *e;
+    int insize, psize, qsize, i;
+
+    /*
+     * Silly special case: if `list' was passed in as NULL, return
+     * NULL immediately.
+     */
+    if (!list) {
+        return NULL;
+    }
+
+    insize = 1;
+
+    while (1) {
+        clist_node_t *tail = NULL;
+        clist_node_t *oldhead = list;
+        p = list;
+        list = NULL;
+
+        int nmerges = 0;  /* count number of merges we do in this pass */
+
+        while (p) {
+            nmerges++;  /* there exists a merge to be done */
+            /* step `insize' places along from p */
+            q = p;
+            psize = 0;
+            for (i = 0; i < insize; i++) {
+                psize++;
+                q = (q->next == oldhead) ? NULL : q->next;
+                if (!q) {
+                    break;
+                }
+            }
+
+            /* if q hasn't fallen off end, we have two lists to merge */
+            qsize = insize;
+
+            /* now we have two lists; merge them */
+            while (psize > 0 || (qsize > 0 && q)) {
+
+                /* decide whether next element of merge comes from p or q */
+                if (psize == 0) {
+                    /* p is empty; e must come from q. */
+                    e = q; q = q->next; qsize--;
+                    if (q == oldhead) {
+                        q = NULL;
+                    }
+                }
+                else if (qsize == 0 || !q) {
+                    /* q is empty; e must come from p. */
+                    e = p; p = p->next; psize--;
+                    if (p == oldhead) {
+                        p = NULL;
+                    }
+                }
+                else if (cmp(p, q) <= 0) {
+                    /* First element of p is lower (or same);
+                     * e must come from p. */
+                    e = p; p = p->next; psize--;
+                    if (p == oldhead) {
+                        p = NULL;
+                    }
+                }
+                else {
+                    /* First element of q is lower; e must come from q. */
+                    e = q; q = q->next; qsize--;
+                    if (q == oldhead) {
+                        q = NULL;
+                    }
+                }
+
+                /* add the next element to the merged list */
+                if (tail) {
+                    tail->next = e;
+                }
+                else {
+                    list = e;
+                }
+                tail = e;
+            }
+
+            /* now p has stepped `insize' places along, and q has too */
+            p = q;
+        }
+
+        /* cppcheck-suppress nullPointer */
+        tail->next = list;
+
+        /* If we have done only one merge, we're finished. */
+        if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
+            return tail;
+        }
+
+        /* Otherwise repeat, merging lists twice the size */
+        insize *= 2;
+    }
+}
diff --git a/core/include/clist.h b/core/include/clist.h
index 81cba1a9f3430a38bd635c927e7c365c75fe039e..2002fc11baffc15c3ecbb4800733c9f99d92f757 100644
--- a/core/include/clist.h
+++ b/core/include/clist.h
@@ -27,6 +27,7 @@
  * clist_find()         | O(n)    | find and return node
  * clist_find_before()  | O(n)    | find node return node pointing to node
  * clist_remove()       | O(n)    | remove and return node
+ * clist_sort()         | O(NlogN)| sort list (stable)
  *
  * clist can be used as a traditional list, a queue (FIFO) and a stack (LIFO) using
  * fast O(1) operations.
@@ -117,7 +118,6 @@ static inline void clist_rpush(clist_node_t *list, clist_node_t *new_node)
     list->next = new_node;
 }
 
-
 /**
  * @brief Inserts *new_node* at the beginning of *list
  *
@@ -342,6 +342,73 @@ static inline void clist_foreach(clist_node_t *list, int(*func)(clist_node_t *))
     } while (node != list->next);
 }
 
+/**
+ * @brief Typedef for comparison function used by @ref clist_sort()
+ *
+ */
+typedef int (*clist_cmp_func_t)(clist_node_t *a, clist_node_t *b);
+
+/**
+ * @brief   List sorting helper function
+ *
+ * @internal
+ *
+ * @param[in]   list    ptr to first element of list
+ * @param[in]   cmp     comparison function
+ *
+ * @returns     ptr to *last* element in list
+ */
+clist_node_t *_clist_sort(clist_node_t *list_head, clist_cmp_func_t cmp);
+
+/**
+ * @brief   Sort a list
+ *
+ * This function will sort @p list using merge sort.
+ * The sorting algorithm runs in O(N log N) time. It is also stable.
+ *
+ * Apart from the to-be-sorted list, the function needs a comparison function.
+ * That function will be called by the sorting implementation for every
+ * comparison.  It gets two pointers a, b of type "clist_node_t" as parameters
+ * and must return
+ * <0, 0 or >0 if a is lesser, equal or larger than b.
+ *
+ * Example:
+ *
+ *     typedef struct {
+ *         clist_node_t next;
+ *         uint32_t value;
+ *     } mylist_node_t;
+ *
+ *     int _cmp(clist_node_t *a, clist_node_t *b)
+ *     {
+ *         uint32_t a_val = ((mylist_node_t *)a)->value;
+ *         uint32_t b_val = ((mylist_node_t *)b)->value;
+ *
+ *         if (a_val < b_val) {
+ *             return -1;
+ *         }
+ *         else if (a_val > b_val) {
+ *             return 1;
+ *         }
+ *         else {
+ *             return 0;
+ *         }
+ *     }
+ *
+ *     ...
+ *
+ *     clist_sort(list, _cmp);
+ *
+ * @param[in,out]   list    List to sort
+ * @param[in]       cmp     Comparison function
+ */
+static inline void clist_sort(clist_node_t *list, clist_cmp_func_t cmp)
+{
+    if (list->next) {
+        list->next = _clist_sort(list->next->next, cmp);
+    }
+}
+
 #ifdef __cplusplus
 }
 #endif