Commit e1564ac3 authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NET]: Add skb_iter functions.

There's no convenient function to walk the data of an skbuff; provide
one.  Netfilter extensions in particular can use this to examine
packet contents without needing an entire copy.

I originally wrote an 'skb_walk(skb, fn, data)' function, but open
iterators are easier to use for complex cases.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarDavid S. Miller <davem@redhat.com>
parent 562d6b23
......@@ -1108,6 +1108,22 @@ extern void skb_split(struct sk_buff *skb,
extern void skb_init(void);
extern void skb_add_mtu(int mtu);
struct skb_iter {
/* Iteration functions set these */
unsigned char *data;
unsigned int len;
/* Private to iteration */
unsigned int nextfrag;
struct sk_buff *fraglist;
};
/* Keep iterating until skb_iter_next returns false. */
extern void skb_iter_first(const struct sk_buff *skb, struct skb_iter *i);
extern int skb_iter_next(const struct sk_buff *skb, struct skb_iter *i);
/* Call this if aborting loop before !skb_iter_next */
extern void skb_iter_abort(const struct sk_buff *skb, struct skb_iter *i);
#ifdef CONFIG_NETFILTER
static inline void nf_conntrack_put(struct nf_ct_info *nfct)
{
......
......@@ -929,6 +929,70 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
return -EFAULT;
}
/* Keep iterating until skb_iter_next returns false. */
void skb_iter_first(const struct sk_buff *skb, struct skb_iter *i)
{
i->len = skb_headlen(skb);
i->data = (unsigned char *)skb->data;
i->nextfrag = 0;
i->fraglist = NULL;
}
int skb_iter_next(const struct sk_buff *skb, struct skb_iter *i)
{
/* Unmap previous, if not head fragment. */
if (i->nextfrag)
kunmap_skb_frag(i->data);
if (i->fraglist) {
fraglist:
/* We're iterating through fraglist. */
if (i->nextfrag < skb_shinfo(i->fraglist)->nr_frags) {
i->data = kmap_skb_frag(&skb_shinfo(i->fraglist)
->frags[i->nextfrag]);
i->len = skb_shinfo(i->fraglist)->frags[i->nextfrag]
.size;
i->nextfrag++;
return 1;
}
/* Fragments with fragments? Too hard! */
BUG_ON(skb_shinfo(i->fraglist)->frag_list);
i->fraglist = i->fraglist->next;
if (!i->fraglist)
goto end;
i->len = skb_headlen(i->fraglist);
i->data = i->fraglist->data;
i->nextfrag = 0;
return 1;
}
if (i->nextfrag < skb_shinfo(skb)->nr_frags) {
i->data = kmap_skb_frag(&skb_shinfo(skb)->frags[i->nextfrag]);
i->len = skb_shinfo(skb)->frags[i->nextfrag].size;
i->nextfrag++;
return 1;
}
i->fraglist = skb_shinfo(skb)->frag_list;
if (i->fraglist)
goto fraglist;
end:
/* Bug trap for callers */
i->data = NULL;
return 0;
}
void skb_iter_abort(const struct sk_buff *skb, struct skb_iter *i)
{
/* Unmap previous, if not head fragment. */
if (i->data && i->nextfrag)
kunmap_skb_frag(i->data);
/* Bug trap for callers */
i->data = NULL;
}
/* Checksum skb data. */
unsigned int skb_checksum(const struct sk_buff *skb, int offset,
......@@ -1399,3 +1463,6 @@ EXPORT_SYMBOL(skb_queue_tail);
EXPORT_SYMBOL(skb_unlink);
EXPORT_SYMBOL(skb_append);
EXPORT_SYMBOL(skb_split);
EXPORT_SYMBOL(skb_iter_first);
EXPORT_SYMBOL(skb_iter_next);
EXPORT_SYMBOL(skb_iter_abort);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment