diff options
-rw-r--r-- | net/sched/cls_flow.c | 67 |
1 files changed, 38 insertions, 29 deletions
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index e17096e3913c..cd709f1294df 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c | |||
@@ -111,44 +111,41 @@ static u32 flow_get_proto(struct sk_buff *skb) | |||
111 | } | 111 | } |
112 | } | 112 | } |
113 | 113 | ||
114 | static int has_ports(u8 protocol) | ||
115 | { | ||
116 | switch (protocol) { | ||
117 | case IPPROTO_TCP: | ||
118 | case IPPROTO_UDP: | ||
119 | case IPPROTO_UDPLITE: | ||
120 | case IPPROTO_SCTP: | ||
121 | case IPPROTO_DCCP: | ||
122 | case IPPROTO_ESP: | ||
123 | return 1; | ||
124 | default: | ||
125 | return 0; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | static u32 flow_get_proto_src(struct sk_buff *skb) | 114 | static u32 flow_get_proto_src(struct sk_buff *skb) |
130 | { | 115 | { |
131 | switch (skb->protocol) { | 116 | switch (skb->protocol) { |
132 | case htons(ETH_P_IP): { | 117 | case htons(ETH_P_IP): { |
133 | struct iphdr *iph; | 118 | struct iphdr *iph; |
119 | int poff; | ||
134 | 120 | ||
135 | if (!pskb_network_may_pull(skb, sizeof(*iph))) | 121 | if (!pskb_network_may_pull(skb, sizeof(*iph))) |
136 | break; | 122 | break; |
137 | iph = ip_hdr(skb); | 123 | iph = ip_hdr(skb); |
138 | if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && | 124 | if (iph->frag_off & htons(IP_MF|IP_OFFSET)) |
139 | has_ports(iph->protocol) && | 125 | break; |
140 | pskb_network_may_pull(skb, iph->ihl * 4 + 2)) | 126 | poff = proto_ports_offset(iph->protocol); |
141 | return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4)); | 127 | if (poff >= 0 && |
128 | pskb_network_may_pull(skb, iph->ihl * 4 + 2 + poff)) { | ||
129 | iph = ip_hdr(skb); | ||
130 | return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 + | ||
131 | poff)); | ||
132 | } | ||
142 | break; | 133 | break; |
143 | } | 134 | } |
144 | case htons(ETH_P_IPV6): { | 135 | case htons(ETH_P_IPV6): { |
145 | struct ipv6hdr *iph; | 136 | struct ipv6hdr *iph; |
137 | int poff; | ||
146 | 138 | ||
147 | if (!pskb_network_may_pull(skb, sizeof(*iph) + 2)) | 139 | if (!pskb_network_may_pull(skb, sizeof(*iph))) |
148 | break; | 140 | break; |
149 | iph = ipv6_hdr(skb); | 141 | iph = ipv6_hdr(skb); |
150 | if (has_ports(iph->nexthdr)) | 142 | poff = proto_ports_offset(iph->nexthdr); |
151 | return ntohs(*(__be16 *)&iph[1]); | 143 | if (poff >= 0 && |
144 | pskb_network_may_pull(skb, sizeof(*iph) + poff + 2)) { | ||
145 | iph = ipv6_hdr(skb); | ||
146 | return ntohs(*(__be16 *)((void *)iph + sizeof(*iph) + | ||
147 | poff)); | ||
148 | } | ||
152 | break; | 149 | break; |
153 | } | 150 | } |
154 | } | 151 | } |
@@ -161,24 +158,36 @@ static u32 flow_get_proto_dst(struct sk_buff *skb) | |||
161 | switch (skb->protocol) { | 158 | switch (skb->protocol) { |
162 | case htons(ETH_P_IP): { | 159 | case htons(ETH_P_IP): { |
163 | struct iphdr *iph; | 160 | struct iphdr *iph; |
161 | int poff; | ||
164 | 162 | ||
165 | if (!pskb_network_may_pull(skb, sizeof(*iph))) | 163 | if (!pskb_network_may_pull(skb, sizeof(*iph))) |
166 | break; | 164 | break; |
167 | iph = ip_hdr(skb); | 165 | iph = ip_hdr(skb); |
168 | if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && | 166 | if (iph->frag_off & htons(IP_MF|IP_OFFSET)) |
169 | has_ports(iph->protocol) && | 167 | break; |
170 | pskb_network_may_pull(skb, iph->ihl * 4 + 4)) | 168 | poff = proto_ports_offset(iph->protocol); |
171 | return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 + 2)); | 169 | if (poff >= 0 && |
170 | pskb_network_may_pull(skb, iph->ihl * 4 + 4 + poff)) { | ||
171 | iph = ip_hdr(skb); | ||
172 | return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 + | ||
173 | 2 + poff)); | ||
174 | } | ||
172 | break; | 175 | break; |
173 | } | 176 | } |
174 | case htons(ETH_P_IPV6): { | 177 | case htons(ETH_P_IPV6): { |
175 | struct ipv6hdr *iph; | 178 | struct ipv6hdr *iph; |
179 | int poff; | ||
176 | 180 | ||
177 | if (!pskb_network_may_pull(skb, sizeof(*iph) + 4)) | 181 | if (!pskb_network_may_pull(skb, sizeof(*iph))) |
178 | break; | 182 | break; |
179 | iph = ipv6_hdr(skb); | 183 | iph = ipv6_hdr(skb); |
180 | if (has_ports(iph->nexthdr)) | 184 | poff = proto_ports_offset(iph->nexthdr); |
181 | return ntohs(*(__be16 *)((void *)&iph[1] + 2)); | 185 | if (poff >= 0 && |
186 | pskb_network_may_pull(skb, sizeof(*iph) + poff + 4)) { | ||
187 | iph = ipv6_hdr(skb); | ||
188 | return ntohs(*(__be16 *)((void *)iph + sizeof(*iph) + | ||
189 | poff + 2)); | ||
190 | } | ||
182 | break; | 191 | break; |
183 | } | 192 | } |
184 | } | 193 | } |