|
@@ -57,6 +57,57 @@
|
|
|
*/
|
|
|
static const char ip_frag_cache_name[] = "ip4-frags";
|
|
|
|
|
|
+/* Use skb->cb to track consecutive/adjacent fragments coming at
|
|
|
+ * the end of the queue. Nodes in the rb-tree queue will
|
|
|
+ * contain "runs" of one or more adjacent fragments.
|
|
|
+ *
|
|
|
+ * Invariants:
|
|
|
+ * - next_frag is NULL at the tail of a "run";
|
|
|
+ * - the head of a "run" has the sum of all fragment lengths in frag_run_len.
|
|
|
+ */
|
|
|
+struct ipfrag_skb_cb {
|
|
|
+ struct inet_skb_parm h;
|
|
|
+ struct sk_buff *next_frag;
|
|
|
+ int frag_run_len;
|
|
|
+};
|
|
|
+
|
|
|
+#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
|
|
|
+
|
|
|
+static void ip4_frag_init_run(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ BUILD_BUG_ON(sizeof(struct ipfrag_skb_cb) > sizeof(skb->cb));
|
|
|
+
|
|
|
+ FRAG_CB(skb)->next_frag = NULL;
|
|
|
+ FRAG_CB(skb)->frag_run_len = skb->len;
|
|
|
+}
|
|
|
+
|
|
|
+/* Append skb to the last "run". */
|
|
|
+static void ip4_frag_append_to_last_run(struct inet_frag_queue *q,
|
|
|
+ struct sk_buff *skb)
|
|
|
+{
|
|
|
+ RB_CLEAR_NODE(&skb->rbnode);
|
|
|
+ FRAG_CB(skb)->next_frag = NULL;
|
|
|
+
|
|
|
+ FRAG_CB(q->last_run_head)->frag_run_len += skb->len;
|
|
|
+ FRAG_CB(q->fragments_tail)->next_frag = skb;
|
|
|
+ q->fragments_tail = skb;
|
|
|
+}
|
|
|
+
|
|
|
+/* Create a new "run" with the skb. */
|
|
|
+static void ip4_frag_create_run(struct inet_frag_queue *q, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ if (q->last_run_head)
|
|
|
+ rb_link_node(&skb->rbnode, &q->last_run_head->rbnode,
|
|
|
+ &q->last_run_head->rbnode.rb_right);
|
|
|
+ else
|
|
|
+ rb_link_node(&skb->rbnode, NULL, &q->rb_fragments.rb_node);
|
|
|
+ rb_insert_color(&skb->rbnode, &q->rb_fragments);
|
|
|
+
|
|
|
+ ip4_frag_init_run(skb);
|
|
|
+ q->fragments_tail = skb;
|
|
|
+ q->last_run_head = skb;
|
|
|
+}
|
|
|
+
|
|
|
/* Describe an entry in the "incomplete datagrams" queue. */
|
|
|
struct ipq {
|
|
|
struct inet_frag_queue q;
|
|
@@ -654,6 +705,28 @@ struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)
|
|
|
}
|
|
|
EXPORT_SYMBOL(ip_check_defrag);
|
|
|
|
|
|
+unsigned int inet_frag_rbtree_purge(struct rb_root *root)
|
|
|
+{
|
|
|
+ struct rb_node *p = rb_first(root);
|
|
|
+ unsigned int sum = 0;
|
|
|
+
|
|
|
+ while (p) {
|
|
|
+ struct sk_buff *skb = rb_entry(p, struct sk_buff, rbnode);
|
|
|
+
|
|
|
+ p = rb_next(p);
|
|
|
+ rb_erase(&skb->rbnode, root);
|
|
|
+ while (skb) {
|
|
|
+ struct sk_buff *next = FRAG_CB(skb)->next_frag;
|
|
|
+
|
|
|
+ sum += skb->truesize;
|
|
|
+ kfree_skb(skb);
|
|
|
+ skb = next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sum;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(inet_frag_rbtree_purge);
|
|
|
+
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
static int dist_min;
|
|
|
|