OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2010 Google Inc. All Rights Reserved. | 2 # Copyright 2010 Google Inc. All Rights Reserved. |
3 # | 3 # |
4 # Licensed under the Apache License, Version 2.0 (the "License"); | 4 # Licensed under the Apache License, Version 2.0 (the "License"); |
5 # you may not use this file except in compliance with the License. | 5 # you may not use this file except in compliance with the License. |
6 # You may obtain a copy of the License at | 6 # You may obtain a copy of the License at |
7 # | 7 # |
8 # http://www.apache.org/licenses/LICENSE-2.0 | 8 # http://www.apache.org/licenses/LICENSE-2.0 |
9 # | 9 # |
10 # Unless required by applicable law or agreed to in writing, software | 10 # Unless required by applicable law or agreed to in writing, software |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
46 _DOWNLOAD_PIPE = '11' # Enforces overall download bandwidth. | 46 _DOWNLOAD_PIPE = '11' # Enforces overall download bandwidth. |
47 _DOWNLOAD_QUEUE = '11' # Shares download bandwidth among destination ports. | 47 _DOWNLOAD_QUEUE = '11' # Shares download bandwidth among destination ports. |
48 _DOWNLOAD_RULE = '5100' # Specifies when the download queue is used. | 48 _DOWNLOAD_RULE = '5100' # Specifies when the download queue is used. |
49 _QUEUE_SLOTS = 100 # Number of packets to queue. | 49 _QUEUE_SLOTS = 100 # Number of packets to queue. |
50 | 50 |
51 _BANDWIDTH_RE = re.compile(BANDWIDTH_PATTERN) | 51 _BANDWIDTH_RE = re.compile(BANDWIDTH_PATTERN) |
52 | 52 |
53 def __init__(self, | 53 def __init__(self, |
54 dont_use=None, | 54 dont_use=None, |
55 host='127.0.0.1', | 55 host='127.0.0.1', |
56 port='80', | 56 ports=None, |
57 ssl_port='443', | |
58 dns_port='53', | |
59 up_bandwidth='0', | 57 up_bandwidth='0', |
60 down_bandwidth='0', | 58 down_bandwidth='0', |
61 delay_ms='0', | 59 delay_ms='0', |
62 packet_loss_rate='0', | 60 packet_loss_rate='0', |
63 init_cwnd='0', | 61 init_cwnd='0', |
64 use_loopback=True): | 62 use_loopback=True): |
65 """Start shaping traffic. | 63 """Start shaping traffic. |
66 | 64 |
67 Args: | 65 Args: |
68 host: a host string (name or IP) for the web proxy. | 66 host: a host string (name or IP) for the web proxy. |
69 port: a port string (e.g. '80') for the web proxy. | 67 ports: a list of ports to shape traffic on. |
70 ssl_port: a port string (e.g. '443') for the SSL web proxy. | |
71 dns_port: a port string for the dns proxy (for unit testing). | |
72 up_bandwidth: Upload bandwidth | 68 up_bandwidth: Upload bandwidth |
73 down_bandwidth: Download bandwidth | 69 down_bandwidth: Download bandwidth |
74 Bandwidths measured in [K|M]{bit/s|Byte/s}. '0' means unlimited. | 70 Bandwidths measured in [K|M]{bit/s|Byte/s}. '0' means unlimited. |
75 delay_ms: Propagation delay in milliseconds. '0' means no delay. | 71 delay_ms: Propagation delay in milliseconds. '0' means no delay. |
76 packet_loss_rate: Packet loss rate in range [0..1]. '0' means no loss. | 72 packet_loss_rate: Packet loss rate in range [0..1]. '0' means no loss. |
77 init_cwnd: the initial cwnd setting. '0' means no change. | 73 init_cwnd: the initial cwnd setting. '0' means no change. |
78 use_loopback: True iff shaping is done on the loopback (or equiv) adapter. | 74 use_loopback: True iff shaping is done on the loopback (or equiv) adapter. |
79 """ | 75 """ |
80 assert dont_use is None # Force args to be named. | 76 assert dont_use is None # Force args to be named. |
81 self.host = host | 77 self.host = host |
82 self.port = port | 78 self.ports = ports |
83 self.ssl_port = ssl_port | |
84 self.dns_port = dns_port | |
85 self.up_bandwidth = up_bandwidth | 79 self.up_bandwidth = up_bandwidth |
86 self.down_bandwidth = down_bandwidth | 80 self.down_bandwidth = down_bandwidth |
87 self.delay_ms = delay_ms | 81 self.delay_ms = delay_ms |
88 self.packet_loss_rate = packet_loss_rate | 82 self.packet_loss_rate = packet_loss_rate |
89 self.init_cwnd = init_cwnd | 83 self.init_cwnd = init_cwnd |
90 self.use_loopback = use_loopback | 84 self.use_loopback = use_loopback |
91 if not self._BANDWIDTH_RE.match(self.up_bandwidth): | 85 if not self._BANDWIDTH_RE.match(self.up_bandwidth): |
92 raise BandwidthValueError(self.up_bandwidth) | 86 raise BandwidthValueError(self.up_bandwidth) |
93 if not self._BANDWIDTH_RE.match(self.down_bandwidth): | 87 if not self._BANDWIDTH_RE.match(self.down_bandwidth): |
94 raise BandwidthValueError(self.down_bandwidth) | 88 raise BandwidthValueError(self.down_bandwidth) |
95 self.is_shaping = False | 89 self.is_shaping = False |
96 | 90 |
97 def __enter__(self): | 91 def __enter__(self): |
98 if self.use_loopback: | 92 if self.use_loopback: |
99 platformsettings.setup_temporary_loopback_config() | 93 platformsettings.setup_temporary_loopback_config() |
100 if self.init_cwnd != '0': | 94 if self.init_cwnd != '0': |
101 platformsettings.set_temporary_tcp_init_cwnd(self.init_cwnd) | 95 platformsettings.set_temporary_tcp_init_cwnd(self.init_cwnd) |
102 try: | 96 try: |
103 ipfw_list = platformsettings.ipfw('list') | 97 ipfw_list = platformsettings.ipfw('list') |
104 if not ipfw_list.startswith('65535 '): | 98 if not ipfw_list.startswith('65535 '): |
105 logging.warn('ipfw has existing rules:\n%s', ipfw_list) | 99 logging.warn('ipfw has existing rules:\n%s', ipfw_list) |
106 self._delete_rules(ipfw_list) | 100 self._delete_rules(ipfw_list) |
107 except: | 101 except: |
108 pass | 102 pass |
109 if (self.up_bandwidth == '0' and self.down_bandwidth == '0' and | 103 if (self.up_bandwidth == '0' and self.down_bandwidth == '0' and |
110 self.delay_ms == '0' and self.packet_loss_rate == '0'): | 104 self.delay_ms == '0' and self.packet_loss_rate == '0'): |
111 logging.info('Skipped shaping traffic.') | 105 logging.info('Skipped shaping traffic.') |
112 return | 106 return |
113 if not self.dns_port and not self.port: | 107 if not self.ports: |
114 raise TrafficShaperException('No ports on which to shape traffic.') | 108 raise TrafficShaperException('No ports on which to shape traffic.') |
115 | 109 |
116 ports = ','.join( | 110 ports = ','.join(str(p) for p in self.ports) |
117 str(p) for p in (self.port, self.ssl_port, self.dns_port) if p) | |
118 half_delay_ms = int(self.delay_ms) / 2 # split over up/down links | 111 half_delay_ms = int(self.delay_ms) / 2 # split over up/down links |
119 | 112 |
120 try: | 113 try: |
121 # Configure upload shaping. | 114 # Configure upload shaping. |
122 platformsettings.ipfw( | 115 platformsettings.ipfw( |
123 'pipe', self._UPLOAD_PIPE, | 116 'pipe', self._UPLOAD_PIPE, |
124 'config', | 117 'config', |
125 'bw', self.up_bandwidth, | 118 'bw', self.up_bandwidth, |
126 'delay', half_delay_ms, | 119 'delay', half_delay_ms, |
127 ) | 120 ) |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 | 177 |
185 def _delete_rules(self, ipfw_list=None): | 178 def _delete_rules(self, ipfw_list=None): |
186 if ipfw_list is None: | 179 if ipfw_list is None: |
187 ipfw_list = platformsettings.ipfw('list') | 180 ipfw_list = platformsettings.ipfw('list') |
188 existing_rules = set( | 181 existing_rules = set( |
189 r.split()[0].lstrip('0') for r in ipfw_list.splitlines()) | 182 r.split()[0].lstrip('0') for r in ipfw_list.splitlines()) |
190 delete_rules = [r for r in (self._DOWNLOAD_RULE, self._UPLOAD_RULE) | 183 delete_rules = [r for r in (self._DOWNLOAD_RULE, self._UPLOAD_RULE) |
191 if r in existing_rules] | 184 if r in existing_rules] |
192 if delete_rules: | 185 if delete_rules: |
193 platformsettings.ipfw('delete', *delete_rules) | 186 platformsettings.ipfw('delete', *delete_rules) |
OLD | NEW |