Skip to content

Commit

Permalink
net: Add function for parsing the header length out of linear etherne…
Browse files Browse the repository at this point in the history
…t frames

This patch updates some of the flow_dissector api so that it can be used to
parse the length of ethernet buffers stored in fragments.  Most of the
changes needed were to __skb_get_poff as it needed to be updated to support
sending a linear buffer instead of a skb.

I have split __skb_get_poff into two functions, the first is skb_get_poff
and it retains the functionality of the original __skb_get_poff.  The other
function is __skb_get_poff which now works much like __skb_flow_dissect in
relation to skb_flow_dissect in that it provides the same functionality but
works with just a data buffer and hlen instead of needing an skb.

Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Alexander Duyck authored and David S. Miller committed Sep 6, 2014
1 parent 2c048e6 commit 56193d1
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 16 deletions.
1 change: 1 addition & 0 deletions include/linux/etherdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <asm/bitsperlong.h>

#ifdef __KERNEL__
u32 eth_get_headlen(void *data, unsigned int max_len);
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
extern const struct header_ops eth_header_ops;

Expand Down
4 changes: 3 additions & 1 deletion include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -3218,7 +3218,9 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off);

int skb_checksum_setup(struct sk_buff *skb, bool recalculate);

u32 __skb_get_poff(const struct sk_buff *skb);
u32 skb_get_poff(const struct sk_buff *skb);
u32 __skb_get_poff(const struct sk_buff *skb, void *data,
const struct flow_keys *keys, int hlen);

/**
* skb_head_is_locked - Determine if the skb->head is locked down
Expand Down
2 changes: 2 additions & 0 deletions include/net/flow_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ static inline __be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8
return __skb_flow_get_ports(skb, thoff, ip_proto, NULL, 0);
}
u32 flow_hash_from_keys(struct flow_keys *keys);
unsigned int flow_get_hlen(const unsigned char *data, unsigned int max_len,
__be16 protocol);
#endif
2 changes: 1 addition & 1 deletion net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ static unsigned int pkt_type_offset(void)

static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
{
return __skb_get_poff((struct sk_buff *)(unsigned long) ctx);
return skb_get_poff((struct sk_buff *)(unsigned long) ctx);
}

static u64 __skb_get_nlattr(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
Expand Down
46 changes: 32 additions & 14 deletions net/core/flow_dissector.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/if_pppox.h>
#include <linux/ppp_defs.h>
#include <net/flow_keys.h>
#include <scsi/fc/fc_fcoe.h>

/* copy saddr & daddr, possibly using 64bit load/store
* Equivalent to : flow->src = iph->saddr;
Expand Down Expand Up @@ -117,6 +118,13 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
nhoff += sizeof(struct ipv6hdr);

/* skip the flow label processing if skb is NULL. The
* assumption here is that if there is no skb we are not
* looking for flow info as much as we are length.
*/
if (!skb)
break;

flow_label = ip6_flowlabel(iph);
if (flow_label) {
/* Awesome, IPv6 packet has a flow label so we can
Expand Down Expand Up @@ -165,6 +173,9 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
return false;
}
}
case htons(ETH_P_FCOE):
flow->thoff = (u16)(nhoff + FCOE_HEADER_LEN);
/* fall through */
default:
return false;
}
Expand Down Expand Up @@ -316,26 +327,18 @@ u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb,
}
EXPORT_SYMBOL(__skb_tx_hash);

/* __skb_get_poff() returns the offset to the payload as far as it could
* be dissected. The main user is currently BPF, so that we can dynamically
* truncate packets without needing to push actual payload to the user
* space and can analyze headers only, instead.
*/
u32 __skb_get_poff(const struct sk_buff *skb)
u32 __skb_get_poff(const struct sk_buff *skb, void *data,
const struct flow_keys *keys, int hlen)
{
struct flow_keys keys;
u32 poff = 0;
u32 poff = keys->thoff;

if (!skb_flow_dissect(skb, &keys))
return 0;

poff += keys.thoff;
switch (keys.ip_proto) {
switch (keys->ip_proto) {
case IPPROTO_TCP: {
const struct tcphdr *tcph;
struct tcphdr _tcph;

tcph = skb_header_pointer(skb, poff, sizeof(_tcph), &_tcph);
tcph = __skb_header_pointer(skb, poff, sizeof(_tcph),
data, hlen, &_tcph);
if (!tcph)
return poff;

Expand Down Expand Up @@ -369,6 +372,21 @@ u32 __skb_get_poff(const struct sk_buff *skb)
return poff;
}

/* skb_get_poff() returns the offset to the payload as far as it could
* be dissected. The main user is currently BPF, so that we can dynamically
* truncate packets without needing to push actual payload to the user
* space and can analyze headers only, instead.
*/
u32 skb_get_poff(const struct sk_buff *skb)
{
struct flow_keys keys;

if (!skb_flow_dissect(skb, &keys))
return 0;

return __skb_get_poff(skb, skb->data, &keys, skb_headlen(skb));
}

static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
{
#ifdef CONFIG_XPS
Expand Down
27 changes: 27 additions & 0 deletions net/ethernet/eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,33 @@ int eth_rebuild_header(struct sk_buff *skb)
}
EXPORT_SYMBOL(eth_rebuild_header);

/**
* eth_get_headlen - determine the the length of header for an ethernet frame
* @data: pointer to start of frame
* @len: total length of frame
*
* Make a best effort attempt to pull the length for all of the headers for
* a given frame in a linear buffer.
*/
u32 eth_get_headlen(void *data, unsigned int len)
{
const struct ethhdr *eth = (const struct ethhdr *)data;
struct flow_keys keys;

/* this should never happen, but better safe than sorry */
if (len < sizeof(*eth))
return len;

/* parse any remaining L2/L3 headers, check for L4 */
if (!__skb_flow_dissect(NULL, &keys, data,
eth->h_proto, sizeof(*eth), len))
return max_t(u32, keys.thoff, sizeof(*eth));

/* parse for any L4 headers */
return min_t(u32, __skb_get_poff(NULL, data, &keys, len), len);
}
EXPORT_SYMBOL(eth_get_headlen);

/**
* eth_type_trans - determine the packet's protocol ID.
* @skb: received socket data
Expand Down

0 comments on commit 56193d1

Please sign in to comment.