8 dstats = collections.defaultdict(int)
9 astats = collections.defaultdict(int)
15 "igmp.ggp.cbt.egp.igp.idrp.mhrp.narp.ospf.eigrp*p1:"
16 "udp.st.nvp.rdp.ddp.pvp.mtp.srp.smp.136:"
18 "ip.gre.etherip.l2tp:"
19 "hopopt.shim6.ipv6.ipv6route.ipv6frag.ipv6icmp.ipv6nonxt.ipv6opts*4:"
33 def _parse_classes(classes):
36 <CLASSLIST> ::= <CLASS> ":" CLASSLIST
38 <CLASS> ::= <PROTOLIST> "*" <PRIORITYSPEC>
40 <DFLTCLASS> ::= "*" <PRIORITYSPEC>
41 <PROTOLIST> ::= <PROTO> "." <PROTOLIST>
43 <PROTO> ::= <NAME> | <NUMBER>
44 <NAME> ::= --see http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers --
45 --only in lowercase, with special characters removed--
48 <PRIORITYSPEC> ::= <THOUGHPUT> [ "#" <SIZE> ] [ "p" <PRIORITY> ]
49 <THOUGHPUT> ::= NUMBER -- default 1
50 <PRIORITY> ::= NUMBER -- default 0
51 <SIZE> ::= NUMBER -- default 1
53 classes = map(lambda x:x.split('*',2),classes.split(':'))
54 priorex = re.compile(r"(?P<thoughput>\d+)?(?:#(?P<size>\d+))?(?:p(?P<priority>\d+))?")
60 prio = priorex.match(cls[1])
65 int(prio.group("thoughput") or 1),
66 int(prio.group("priority") or 0),
67 int(prio.group("size") or 1),
70 cls[0] = map(clsmap, cls[0].split('.'))
77 class ClassQueue(object):
83 self.classspec = _parse_classes(_classes)
85 self.queues = [ collections.deque() for cls in xrange(len(self.classspec)) ]
89 for cls, (protos, (thoughput, prio, size)) in enumerate(self.classspec)
95 for cls in xrange(len(self.classspec))
96 for protos, (thoughput, prio, size) in ( self.classspec[cls], )
101 for cls in xrange(len(self.classspec))
102 for protos, (thoughput, prio, size) in ( self.classspec[cls], )
107 for cls, (protos, (thoughput, prio, size)) in enumerate(self.classspec)
108 for i in xrange(thoughput)
111 filter(lambda x : self.priomap[x] == prio, order)
112 for prio in reversed(sorted(set(self.priomap)))
114 for order in self.order:
115 random.shuffle(order)
117 if None not in self.classmap:
118 raise RuntimeError, "No default class: a default class must be present"
121 self.queues.append(collections.deque())
122 self.priomap.append(-1)
123 self.sizemap.append(_size)
124 self.order.insert(0, [len(self.queues)-1])
129 def __nonzero__(self):
139 self.cycle_update = True
141 self.queues = [ collections.deque() for cls in xrange(len(self.classspec)) ]
143 def queuefor(self, packet, ord=ord, len=len, classmask=0xEC):
144 if len(packet) >= 10:
145 proto = ord(packet[9])
146 rv = self.classmap.get(proto)
148 rv = self.classmap.get(None)
151 rv = self.classmap.get(None)
152 return proto, rv, self.sizemap[rv]
154 def get_packetdrop_p(self, qlen, qsize, packet):
155 pdrop = ((qlen * 1.0 / qsize) - 0.5) * 2.0
159 def append(self, packet, len=len, dstats=dstats, astats=astats, rng=random.random):
160 proto,qi,size = self.queuefor(packet)
165 if lq > (size/2) and _red:
166 pdrop = self.get_packetdrop_p(lq, size, packet)
170 classes = self.classes
171 if qi not in classes:
173 self.cycle_update = True
186 def appendleft(self, packet):
187 self.queues[-1].append(packet)
190 def pop(self, xrange=xrange, len=len, iter=iter, pop=collections.deque.pop):
191 return self.popleft(pop=pop)
193 def popleft(self, xrange=xrange, len=len, iter=iter, enumerate=enumerate, zip=zip, pop=collections.deque.popleft):
195 classes = self.classes
198 # shortcut for non-tos traffic
199 rv = pop(queues[iter(classes).next()])
203 if self.cycle_update:
205 filter(classes.__contains__, order)
206 for order in self.order
208 self.cycle = map(itertools.cycle, cycle)
209 self.cyclelen = map(len,cycle)
210 self.cycle_update = False
212 for prio, (cycle, cyclelen) in enumerate(zip(self.cycle, self.cyclelen)):
214 for i in xrange(cyclelen):
223 # Needs to update the cycle
225 self.cycle_update = True
227 raise IndexError, "pop from an empty queue"
229 def dump_stats(self, astats=astats, dstats=dstats, dump_count=dump_count):
230 if dump_count[0] >= 10000:
231 dstatsstr = "".join(['%s:%s\n' % (key, value) for key, value in dstats.items()])
232 astatsstr = "".join(['%s:%s\n' % (key, value) for key, value in astats.items()])
233 fd = open('dropped_stats', 'w')
234 iovec.writev(fd.fileno(), "Dropped:\n", dstatsstr, "Accepted:\n", astatsstr)
240 queueclass = ClassQueue
242 def init(size = 1000, classes = _classes, logdropped = 'False', red = True):
243 global _size, _classes, _logdropped
247 _logdropped = logdropped.lower() in ('true','1','on')
251 open('dropped_stats', 'w').close()