|  | #include <linux/skbuff.h> | 
|  | #include <linux/export.h> | 
|  | #include <linux/ip.h> | 
|  | #include <linux/ipv6.h> | 
|  | #include <linux/if_vlan.h> | 
|  | #include <net/ip.h> | 
|  | #include <linux/if_tunnel.h> | 
|  | #include <linux/if_pppox.h> | 
|  | #include <linux/ppp_defs.h> | 
|  | #include <net/flow_keys.h> | 
|  |  | 
|  | /* copy saddr & daddr, possibly using 64bit load/store | 
|  | * Equivalent to :	flow->src = iph->saddr; | 
|  | *			flow->dst = iph->daddr; | 
|  | */ | 
|  | static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph) | 
|  | { | 
|  | BUILD_BUG_ON(offsetof(typeof(*flow), dst) != | 
|  | offsetof(typeof(*flow), src) + sizeof(flow->src)); | 
|  | memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst)); | 
|  | } | 
|  |  | 
|  | bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow) | 
|  | { | 
|  | int poff, nhoff = skb_network_offset(skb); | 
|  | u8 ip_proto; | 
|  | __be16 proto = skb->protocol; | 
|  |  | 
|  | memset(flow, 0, sizeof(*flow)); | 
|  |  | 
|  | again: | 
|  | switch (proto) { | 
|  | case __constant_htons(ETH_P_IP): { | 
|  | const struct iphdr *iph; | 
|  | struct iphdr _iph; | 
|  | ip: | 
|  | iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); | 
|  | if (!iph) | 
|  | return false; | 
|  |  | 
|  | if (ip_is_fragment(iph)) | 
|  | ip_proto = 0; | 
|  | else | 
|  | ip_proto = iph->protocol; | 
|  | iph_to_flow_copy_addrs(flow, iph); | 
|  | nhoff += iph->ihl * 4; | 
|  | break; | 
|  | } | 
|  | case __constant_htons(ETH_P_IPV6): { | 
|  | const struct ipv6hdr *iph; | 
|  | struct ipv6hdr _iph; | 
|  | ipv6: | 
|  | iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); | 
|  | if (!iph) | 
|  | return false; | 
|  |  | 
|  | ip_proto = iph->nexthdr; | 
|  | flow->src = iph->saddr.s6_addr32[3]; | 
|  | flow->dst = iph->daddr.s6_addr32[3]; | 
|  | nhoff += sizeof(struct ipv6hdr); | 
|  | break; | 
|  | } | 
|  | case __constant_htons(ETH_P_8021Q): { | 
|  | const struct vlan_hdr *vlan; | 
|  | struct vlan_hdr _vlan; | 
|  |  | 
|  | vlan = skb_header_pointer(skb, nhoff, sizeof(_vlan), &_vlan); | 
|  | if (!vlan) | 
|  | return false; | 
|  |  | 
|  | proto = vlan->h_vlan_encapsulated_proto; | 
|  | nhoff += sizeof(*vlan); | 
|  | goto again; | 
|  | } | 
|  | case __constant_htons(ETH_P_PPP_SES): { | 
|  | struct { | 
|  | struct pppoe_hdr hdr; | 
|  | __be16 proto; | 
|  | } *hdr, _hdr; | 
|  | hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); | 
|  | if (!hdr) | 
|  | return false; | 
|  | proto = hdr->proto; | 
|  | nhoff += PPPOE_SES_HLEN; | 
|  | switch (proto) { | 
|  | case __constant_htons(PPP_IP): | 
|  | goto ip; | 
|  | case __constant_htons(PPP_IPV6): | 
|  | goto ipv6; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  | default: | 
|  | return false; | 
|  | } | 
|  |  | 
|  | switch (ip_proto) { | 
|  | case IPPROTO_GRE: { | 
|  | struct gre_hdr { | 
|  | __be16 flags; | 
|  | __be16 proto; | 
|  | } *hdr, _hdr; | 
|  |  | 
|  | hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); | 
|  | if (!hdr) | 
|  | return false; | 
|  | /* | 
|  | * Only look inside GRE if version zero and no | 
|  | * routing | 
|  | */ | 
|  | if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) { | 
|  | proto = hdr->proto; | 
|  | nhoff += 4; | 
|  | if (hdr->flags & GRE_CSUM) | 
|  | nhoff += 4; | 
|  | if (hdr->flags & GRE_KEY) | 
|  | nhoff += 4; | 
|  | if (hdr->flags & GRE_SEQ) | 
|  | nhoff += 4; | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | } | 
|  | case IPPROTO_IPIP: | 
|  | goto again; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | flow->ip_proto = ip_proto; | 
|  | poff = proto_ports_offset(ip_proto); | 
|  | if (poff >= 0) { | 
|  | __be32 *ports, _ports; | 
|  |  | 
|  | nhoff += poff; | 
|  | ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports); | 
|  | if (ports) | 
|  | flow->ports = *ports; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | EXPORT_SYMBOL(skb_flow_dissect); |