diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-11-28 23:22:15 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-11-29 13:17:03 -0500 |
commit | 2bcc34bb987e07abcf6bc30b7f92fbf22b59d4ef (patch) | |
tree | 52d5f76d4821e9cf6a6fb08f8680febf6708bf43 /net/sched | |
parent | 11fca931d35a34d01ce209eb8d51ff667c9f5e7c (diff) |
sch_choke: use skb_flow_dissect()
Instead of using a custom flow dissector, use skb_flow_dissect() and
benefit from tunnelling support.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/sch_choke.c | 120 |
1 files changed, 31 insertions, 89 deletions
diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 061bcb744bbd..205d369a217c 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c | |||
@@ -19,10 +19,7 @@ | |||
19 | #include <net/pkt_sched.h> | 19 | #include <net/pkt_sched.h> |
20 | #include <net/inet_ecn.h> | 20 | #include <net/inet_ecn.h> |
21 | #include <net/red.h> | 21 | #include <net/red.h> |
22 | #include <linux/ip.h> | 22 | #include <net/flow_keys.h> |
23 | #include <net/ip.h> | ||
24 | #include <linux/ipv6.h> | ||
25 | #include <net/ipv6.h> | ||
26 | 23 | ||
27 | /* | 24 | /* |
28 | CHOKe stateless AQM for fair bandwidth allocation | 25 | CHOKe stateless AQM for fair bandwidth allocation |
@@ -142,92 +139,10 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx) | |||
142 | --sch->q.qlen; | 139 | --sch->q.qlen; |
143 | } | 140 | } |
144 | 141 | ||
145 | /* | ||
146 | * Compare flow of two packets | ||
147 | * Returns true only if source and destination address and port match. | ||
148 | * false for special cases | ||
149 | */ | ||
150 | static bool choke_match_flow(struct sk_buff *skb1, | ||
151 | struct sk_buff *skb2) | ||
152 | { | ||
153 | int off1, off2, poff; | ||
154 | const u32 *ports1, *ports2; | ||
155 | u32 _ports1, _ports2; | ||
156 | u8 ip_proto; | ||
157 | __u32 hash1; | ||
158 | |||
159 | if (skb1->protocol != skb2->protocol) | ||
160 | return false; | ||
161 | |||
162 | /* Use rxhash value as quick check */ | ||
163 | hash1 = skb_get_rxhash(skb1); | ||
164 | if (!hash1 || hash1 != skb_get_rxhash(skb2)) | ||
165 | return false; | ||
166 | |||
167 | /* Probably match, but be sure to avoid hash collisions */ | ||
168 | off1 = skb_network_offset(skb1); | ||
169 | off2 = skb_network_offset(skb2); | ||
170 | |||
171 | switch (skb1->protocol) { | ||
172 | case __constant_htons(ETH_P_IP): { | ||
173 | const struct iphdr *ip1, *ip2; | ||
174 | struct iphdr _ip1, _ip2; | ||
175 | |||
176 | ip1 = skb_header_pointer(skb1, off1, sizeof(_ip1), &_ip1); | ||
177 | ip2 = skb_header_pointer(skb2, off2, sizeof(_ip2), &_ip2); | ||
178 | if (!ip1 || !ip2) | ||
179 | return false; | ||
180 | ip_proto = ip1->protocol; | ||
181 | if (ip_proto != ip2->protocol || | ||
182 | ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr) | ||
183 | return false; | ||
184 | |||
185 | if (ip_is_fragment(ip1) | ip_is_fragment(ip2)) | ||
186 | ip_proto = 0; | ||
187 | off1 += ip1->ihl * 4; | ||
188 | off2 += ip2->ihl * 4; | ||
189 | break; | ||
190 | } | ||
191 | |||
192 | case __constant_htons(ETH_P_IPV6): { | ||
193 | const struct ipv6hdr *ip1, *ip2; | ||
194 | struct ipv6hdr _ip1, _ip2; | ||
195 | |||
196 | ip1 = skb_header_pointer(skb1, off1, sizeof(_ip1), &_ip1); | ||
197 | ip2 = skb_header_pointer(skb2, off2, sizeof(_ip2), &_ip2); | ||
198 | if (!ip1 || !ip2) | ||
199 | return false; | ||
200 | |||
201 | ip_proto = ip1->nexthdr; | ||
202 | if (ip_proto != ip2->nexthdr || | ||
203 | ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) || | ||
204 | ipv6_addr_cmp(&ip1->daddr, &ip2->daddr)) | ||
205 | return false; | ||
206 | off1 += 40; | ||
207 | off2 += 40; | ||
208 | } | ||
209 | |||
210 | default: /* Maybe compare MAC header here? */ | ||
211 | return false; | ||
212 | } | ||
213 | |||
214 | poff = proto_ports_offset(ip_proto); | ||
215 | if (poff < 0) | ||
216 | return true; | ||
217 | |||
218 | off1 += poff; | ||
219 | off2 += poff; | ||
220 | |||
221 | ports1 = skb_header_pointer(skb1, off1, sizeof(_ports1), &_ports1); | ||
222 | ports2 = skb_header_pointer(skb2, off2, sizeof(_ports2), &_ports2); | ||
223 | if (!ports1 || !ports2) | ||
224 | return false; | ||
225 | |||
226 | return *ports1 == *ports2; | ||
227 | } | ||
228 | |||
229 | struct choke_skb_cb { | 142 | struct choke_skb_cb { |
230 | u16 classid; | 143 | u16 classid; |
144 | u8 keys_valid; | ||
145 | struct flow_keys keys; | ||
231 | }; | 146 | }; |
232 | 147 | ||
233 | static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) | 148 | static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) |
@@ -248,6 +163,32 @@ static u16 choke_get_classid(const struct sk_buff *skb) | |||
248 | } | 163 | } |
249 | 164 | ||
250 | /* | 165 | /* |
166 | * Compare flow of two packets | ||
167 | * Returns true only if source and destination address and port match. | ||
168 | * false for special cases | ||
169 | */ | ||
170 | static bool choke_match_flow(struct sk_buff *skb1, | ||
171 | struct sk_buff *skb2) | ||
172 | { | ||
173 | if (skb1->protocol != skb2->protocol) | ||
174 | return false; | ||
175 | |||
176 | if (!choke_skb_cb(skb1)->keys_valid) { | ||
177 | choke_skb_cb(skb1)->keys_valid = 1; | ||
178 | skb_flow_dissect(skb1, &choke_skb_cb(skb1)->keys); | ||
179 | } | ||
180 | |||
181 | if (!choke_skb_cb(skb2)->keys_valid) { | ||
182 | choke_skb_cb(skb2)->keys_valid = 1; | ||
183 | skb_flow_dissect(skb2, &choke_skb_cb(skb2)->keys); | ||
184 | } | ||
185 | |||
186 | return !memcmp(&choke_skb_cb(skb1)->keys, | ||
187 | &choke_skb_cb(skb2)->keys, | ||
188 | sizeof(struct flow_keys)); | ||
189 | } | ||
190 | |||
191 | /* | ||
251 | * Classify flow using either: | 192 | * Classify flow using either: |
252 | * 1. pre-existing classification result in skb | 193 | * 1. pre-existing classification result in skb |
253 | * 2. fast internal classification | 194 | * 2. fast internal classification |
@@ -333,6 +274,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
333 | goto other_drop; /* Packet was eaten by filter */ | 274 | goto other_drop; /* Packet was eaten by filter */ |
334 | } | 275 | } |
335 | 276 | ||
277 | choke_skb_cb(skb)->keys_valid = 0; | ||
336 | /* Compute average queue usage (see RED) */ | 278 | /* Compute average queue usage (see RED) */ |
337 | p->qavg = red_calc_qavg(p, sch->q.qlen); | 279 | p->qavg = red_calc_qavg(p, sch->q.qlen); |
338 | if (red_is_idling(p)) | 280 | if (red_is_idling(p)) |