aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-11-28 23:22:15 -0500
committerDavid S. Miller <davem@davemloft.net>2011-11-29 13:17:03 -0500
commit2bcc34bb987e07abcf6bc30b7f92fbf22b59d4ef (patch)
tree52d5f76d4821e9cf6a6fb08f8680febf6708bf43 /net/sched
parent11fca931d35a34d01ce209eb8d51ff667c9f5e7c (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.c120
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 */
150static 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
229struct choke_skb_cb { 142struct choke_skb_cb {
230 u16 classid; 143 u16 classid;
144 u8 keys_valid;
145 struct flow_keys keys;
231}; 146};
232 147
233static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) 148static 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 */
170static 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))