DummynetPipe.cc 8.1 KB
Newer Older
1
2
// DummynetPipe.cc

3
4
#ifdef FREEBSD

5
6
extern "C"
{
7
8
#include <sys/types.h>
#include <sys/socket.h>
9
10
11
#include <sys/param.h>
#include <sys/mbuf.h>
#include <unistd.h>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
#include <netinet/ip_compat.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_fw.h>
#include <netinet/ip_dummynet.h>
*/


#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_fw.h>
#include <net/route.h> /* def. of struct route */
#include <netinet/ip_dummynet.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
}

34
35
36
37
#include "lib.hh"
#include "DummynetPipe.hh"


38
39
40
41
42
#if __FreeBSD_version >= 601000
#define DN_PIPE_NEXT(p) ((p)->next.sle_next)
#else
#define DN_PIPE_NEXT(p) ((p)->next)
#endif
43
44
45
46
47

using namespace std;

DummynetPipe::DummynetPipe(std::string const & pipeno)
{
48
  dummynetPipeNumber = stringToInt(pipeno);
49

50
51
52
53
54
  dummynetSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  if (dummynetSocket < 0)
  {
    cerr << "Can't create raw socket" << endl;
  }
55
56
57
58
}

DummynetPipe::~DummynetPipe()
{
59
  close(dummynetSocket);
60
61
}

62
void DummynetPipe::reset(void)
63
{
64
  char *data;
65
  struct dn_pipe *pipe;
66

67
68
69
70
71
72
  data = getAllPipes();
  if (data == NULL) {
    cerr << "Couldn't get dummynet pipes" << endl;
    return;
  }
  pipe = findPipe(data);
73
74
  if (pipe == NULL)
  {
75
    cerr << "Couldn't find pipe " << dummynetPipeNumber << endl;
76
77
    return;
  }
78

79
80
  map<Parameter::ParameterType, Parameter>::iterator pos = g::defaultParameters.begin();
  map<Parameter::ParameterType, Parameter>::iterator limit = g::defaultParameters.end();
81

82
83
84
85
  for (; pos != limit; ++pos)
  {
    updateParameter(pipe, pos->second);
  }
86

87
  setPipe(pipe);
88
89
}

90
void DummynetPipe::updateParameter(struct dn_pipe* pipe, Parameter const & newParameter)
91
{
92
93
94
95
96
97
98
99
100
101
102
103
  switch (newParameter.getType())
  {
  case Parameter::BANDWIDTH:
    pipe->bandwidth = newParameter.getValue();
    break;
  case Parameter::DELAY:
    pipe->delay = newParameter.getValue();
    break;
  default:
    // TODO: Handle UP and DOWN events
    break;
  }
104
105
}

106
void DummynetPipe::resetParameter(Parameter const & newParameter)
107
{
108
  char *data;
109
  struct dn_pipe *pipe;
110

111
112
113
114
115
116
  data = getAllPipes();
  if (data == NULL) {
    cerr << "Couldn't get dummynet pipes" << endl;
    return;
  }
  pipe = findPipe(data);
117
118
  if (pipe == NULL)
  {
119
    cerr << "Couldn't find pipe " << dummynetPipeNumber << endl;
120
121
    return;
  }
122

123
  updateParameter(pipe, newParameter);
124

125
  setPipe(pipe);
126
127
}

128
char * DummynetPipe::callGetsockopt(char * data, size_t * count)
129
{
130
131
132
133
134
135
136
137
138
139
140
  if (getsockopt(dummynetSocket, IPPROTO_IP, IP_DUMMYNET_GET,
                 data, count) < 0)
  {
    cerr << "Error in getsockopt\n" << endl;
    return NULL;
  }
  else
  {
    return data;
  }
}
141

142
143
144
145
146
147
148
149
150
151
152
153
154
char * DummynetPipe::getAllPipes(void)
{
  static vector<char> data(sizeof(struct dn_pipe));
  char * result = & data[0];
  size_t num_bytes = 0;
  result = callGetsockopt(& data[0], &num_bytes);
  while (num_bytes >= data.size() && result != NULL)
  {
    data.resize(data.size()*2 + 200);
    result = callGetsockopt(& data[0], &num_bytes);
  }
  return result;
}
155

156
struct dn_pipe * DummynetPipe::findPipe(char *data)
157
{
158
  struct dn_pipe *p, *pipe;
159
  int l;
160
161
162
  
  pipe = NULL;
  p = (struct dn_pipe *) data;
163

164
  while ( DN_PIPE_NEXT(p) == (struct dn_pipe *)DN_IS_PIPE )
165
  {
166
167
168
    if (dummynetPipeNumber == p->pipe_nr) {
      pipe = p;
      break;
169
170
    }

171
172
173
174
    l = p->fs.rq_elements * sizeof(struct dn_flow_queue) +
        sizeof(struct dn_pipe);

    p = (struct dn_pipe *)((char *)p + l);
175

176
  }
177

178
179
  return pipe;
}
180

181
182


183
184
185
186
187
188
void DummynetPipe::setPipe(struct dn_pipe *pipe)
{
  if (setsockopt(dummynetSocket, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, pipe, sizeof(*pipe))
      < 0)
    cerr << "IP_DUMMYNET_CONFIGURE setsockopt failed" << endl;
}
189

190
191
#endif
#ifdef LINUX
192

193
// NetlinkPipe.cc
194

195
#include "lib.hh"
196
#include "DummynetPipe.hh"
197

198
199
200
201
extern "C"
{
#include <sys/types.h>
#include <sys/socket.h>
202

203
204
#include <netlink/list.h>
#include <netlink/object.h>
205
206
207
208
#include <netlink/route/rtnl.h>
#include <netlink/route/route.h>
#include <netlink/route/link.h>
#include <netlink/route/class.h>
209
210
211
212
213
#include <netlink/route/qdisc.h>
#include <netlink/route/sch/plr.h>
#include <netlink/route/sch/delay.h>
#include <netlink/route/sch/htb.h>
}
214

215
using namespace std;
216

217
218
219
220
221
222
223
224
225
NetlinkPipe::NetlinkPipe(std::string const & iface, std::string const & pipeno)
{
	interfaceName = iface;
	pipeNumber = pipeno;
	nl_handle = NULL;
	class_cache = NULL;
	qdisc_cache = NULL;
	
	init();
226
227
228
229
230
231
232
233
	/* test(); */
}

void NetlinkPipe::test(void)
{
	Parameter testParam(Parameter::LOSS, 50000);

	updateParameter(testParam);
234
235
236
237
238
239
240
241
}

int NetlinkPipe::init(void)
{
	struct nl_cache *link_cache;
	int handle;
	string str;

242
243
	link_cache = NULL;

244
245
246
247
	cerr << "Got pipe number " << pipeNumber << endl;
	handle = hexStringToInt(pipeNumber);
	cerr << "handle: " << handle << endl;

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
	nl_handle = nl_handle_alloc();
	if (nl_handle == NULL) {
		cerr << "Unable to allocate nl_handle" << endl;
		return -1;
	}

	if (nl_connect(nl_handle, NETLINK_ROUTE) < 0) {
		cerr << "Unable to allocate Netlink socket" << endl;
		return -1;
	}
	link_cache = rtnl_link_alloc_cache(nl_handle);
	if (link_cache == NULL) {
		cerr << "Unable to allocate link cache" << endl;
		return -1;
	}

264
	ifindex = rtnl_link_name2i(link_cache, interfaceName.c_str());
265
266
267
268
269
270
271
272
273
274
275
	if (ifindex == RTNL_LINK_NOT_FOUND) {
		cerr << "Unable to translate link name to ifindex" << endl;
		return -1;
	}
	nl_cache_free(link_cache);

	qdisc_cache = rtnl_qdisc_alloc_cache(nl_handle);
	if (qdisc_cache == NULL) {
		cerr << "Unable to allocate qdisc cache" << endl;
		return -1;
	}
276
277
278
279
280

	plrHandle = handle << 16;
	delayHandle = (handle + 0x10) << 16;
	htbHandle = (handle + 0x20) << 16;

281
282
283
284
285
286
287
	class_cache = rtnl_class_alloc_cache(nl_handle, ifindex);
	if (class_cache == NULL) {
		cerr << "Unable to allocate class cache" << endl;
		return -1;
	}

	htbClassHandle = htbHandle + 1;
288

289

290
	return 0;
291
292
}

293
294
295
296
NetlinkPipe::~NetlinkPipe()
{
	if (qdisc_cache)
		nl_cache_free(qdisc_cache);
297

298
299
	if (class_cache)
		nl_cache_free(class_cache);
300

301
302
303
304
	if (nl_handle)
		nl_close(nl_handle);
}

305
void NetlinkPipe::reset(void)
306
{
307
308
309
310
311
	map<Parameter::ParameterType, Parameter>::iterator pos = g::defaultParameters.begin();
	map<Parameter::ParameterType, Parameter>::iterator limit = g::defaultParameters.end();

	for (; pos != limit; ++pos)
	{
312
		updateParameter(pos->second);
313
314
315
	}
}

316
317
318
319
320
321
void NetlinkPipe::resetParameter(Parameter const & newParameter)
{
	updateParameter(newParameter);
}

void NetlinkPipe::updateParameter(Parameter const & newParameter)
322
323
324
325
326
327
328
{
	struct rtnl_qdisc *qdisc;
	struct rtnl_class *htbClass;

	qdisc = NULL;

	switch(newParameter.getType()) {
329
		case Parameter::BANDWIDTH:
330
331
				htbClass = rtnl_class_get(class_cache, htbClassHandle);
				if (htbClass == NULL) {
332
					cerr << "Couldn't find htb class " << htbClassHandle << endl;
333
334
335
336
					return;
				}
				rtnl_htb_set_rate(htbClass, newParameter.getValue());
				rtnl_htb_set_ceil(htbClass, newParameter.getValue());
337
338
				rtnl_class_change(nl_handle, htbClass, NULL);
				rtnl_class_put(htbClass);
339
                                break;
340
		case Parameter::DELAY:
341
342
				qdisc = rtnl_qdisc_get(qdisc_cache, ifindex, delayHandle);
				if (qdisc == NULL) {
343
					cerr << "Couldn't find delay qdisc " << delayHandle << endl;
344
345
346
					return;
				}
				rtnl_delay_set_delay(qdisc, newParameter.getValue());
347
				rtnl_qdisc_change(nl_handle, qdisc, NULL);
348
349
				rtnl_qdisc_put(qdisc);
                                break;
350
		case Parameter::LOSS:
351
				qdisc = rtnl_qdisc_get(qdisc_cache, ifindex, plrHandle);
352
				if (qdisc == NULL) {
353
					cerr << "Couldn't find plr qdisc " << plrHandle << endl;
354
355
356
					return;
				}
				rtnl_plr_set_plr(qdisc, newParameter.getValue());
357
				rtnl_qdisc_change(nl_handle, qdisc, NULL);
358
359
				rtnl_qdisc_put(qdisc);
                                break;
Ryan Jackson's avatar
Ryan Jackson committed
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
		case Parameter::LINK_UP:
				uint32_t value;

				if (newParameter.getValue()) {
					value = 1;
				}
				else {
					value = 0x7ffffffff;
				}
				qdisc = rtnl_qdisc_get(qdisc_cache, ifindex, plrHandle);
				if (qdisc == NULL) {
					cerr << "Couldn't find plr qdisc " << plrHandle << endl;
					return;
				}
				rtnl_plr_set_plr(qdisc, value);
				rtnl_qdisc_change(nl_handle, qdisc, NULL);
				rtnl_qdisc_put(qdisc);
                                break;
378
379
		default:
				break;
380
381
382
	}
}

383
#endif