1 #include <linux/skbuff.h> 2 #include <linux/export.h> 3 #include <linux/ip.h> 4 #include <linux/ipv6.h> 5 #include <linux/if_vlan.h> 6 #include <net/ip.h> 7 #include <linux/if_tunnel.h> 8 #include <linux/if_pppox.h> 9 #include <linux/ppp_defs.h> 10 #include <net/flow_keys.h> 11 12 /* copy saddr & daddr, possibly using 64bit load/store 13 * Equivalent to : flow->src = iph->saddr; 14 * flow->dst = iph->daddr; 15 */ 16 static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph) 17 { 18 BUILD_BUG_ON(offsetof(typeof(*flow), dst) != 19 offsetof(typeof(*flow), src) + sizeof(flow->src)); 20 memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst)); 21 } 22 23 bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow) 24 { 25 int poff, nhoff = skb_network_offset(skb); 26 u8 ip_proto; 27 __be16 proto = skb->protocol; 28 29 memset(flow, 0, sizeof(*flow)); 30 31 again: 32 switch (proto) { 33 case __constant_htons(ETH_P_IP): { 34 const struct iphdr *iph; 35 struct iphdr _iph; 36 ip: 37 iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); 38 if (!iph) 39 return false; 40 41 if (ip_is_fragment(iph)) 42 ip_proto = 0; 43 else 44 ip_proto = iph->protocol; 45 iph_to_flow_copy_addrs(flow, iph); 46 nhoff += iph->ihl * 4; 47 break; 48 } 49 case __constant_htons(ETH_P_IPV6): { 50 const struct ipv6hdr *iph; 51 struct ipv6hdr _iph; 52 ipv6: 53 iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); 54 if (!iph) 55 return false; 56 57 ip_proto = iph->nexthdr; 58 flow->src = iph->saddr.s6_addr32[3]; 59 flow->dst = iph->daddr.s6_addr32[3]; 60 nhoff += sizeof(struct ipv6hdr); 61 break; 62 } 63 case __constant_htons(ETH_P_8021Q): { 64 const struct vlan_hdr *vlan; 65 struct vlan_hdr _vlan; 66 67 vlan = skb_header_pointer(skb, nhoff, sizeof(_vlan), &_vlan); 68 if (!vlan) 69 return false; 70 71 proto = vlan->h_vlan_encapsulated_proto; 72 nhoff += sizeof(*vlan); 73 goto again; 74 } 75 case __constant_htons(ETH_P_PPP_SES): { 76 struct { 77 struct pppoe_hdr hdr; 78 __be16 proto; 79 } *hdr, _hdr; 80 hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); 81 if (!hdr) 82 return false; 83 proto = hdr->proto; 84 nhoff += PPPOE_SES_HLEN; 85 switch (proto) { 86 case __constant_htons(PPP_IP): 87 goto ip; 88 case __constant_htons(PPP_IPV6): 89 goto ipv6; 90 default: 91 return false; 92 } 93 } 94 default: 95 return false; 96 } 97 98 switch (ip_proto) { 99 case IPPROTO_GRE: { 100 struct gre_hdr { 101 __be16 flags; 102 __be16 proto; 103 } *hdr, _hdr; 104 105 hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); 106 if (!hdr) 107 return false; 108 /* 109 * Only look inside GRE if version zero and no 110 * routing 111 */ 112 if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) { 113 proto = hdr->proto; 114 nhoff += 4; 115 if (hdr->flags & GRE_CSUM) 116 nhoff += 4; 117 if (hdr->flags & GRE_KEY) 118 nhoff += 4; 119 if (hdr->flags & GRE_SEQ) 120 nhoff += 4; 121 goto again; 122 } 123 break; 124 } 125 case IPPROTO_IPIP: 126 goto again; 127 default: 128 break; 129 } 130 131 flow->ip_proto = ip_proto; 132 poff = proto_ports_offset(ip_proto); 133 if (poff >= 0) { 134 __be32 *ports, _ports; 135 136 nhoff += poff; 137 ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports); 138 if (ports) 139 flow->ports = *ports; 140 } 141 142 return true; 143 } 144 EXPORT_SYMBOL(skb_flow_dissect); 145