Config: Cause a fatal() when a parameter without a default value isn't set(FS #315).
[gem5.git] / src / dev / ethertap.cc
1 /*
2 * Copyright (c) 2003-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Nathan Binkert
29 */
30
31 /* @file
32 * Interface to connect a simulated ethernet device to the real world
33 */
34
35 #if defined(__OpenBSD__) || defined(__APPLE__)
36 #include <sys/param.h>
37 #endif
38 #include <netinet/in.h>
39
40 #include <unistd.h>
41
42 #include <deque>
43 #include <string>
44
45 #include "base/misc.hh"
46 #include "base/pollevent.hh"
47 #include "base/socket.hh"
48 #include "base/trace.hh"
49 #include "dev/etherdump.hh"
50 #include "dev/etherint.hh"
51 #include "dev/etherpkt.hh"
52 #include "dev/ethertap.hh"
53
54 using namespace std;
55
56 /**
57 */
58 class TapListener
59 {
60 protected:
61 /**
62 */
63 class Event : public PollEvent
64 {
65 protected:
66 TapListener *listener;
67
68 public:
69 Event(TapListener *l, int fd, int e)
70 : PollEvent(fd, e), listener(l) {}
71
72 virtual void process(int revent) { listener->accept(); }
73 };
74
75 friend class Event;
76 Event *event;
77
78 protected:
79 ListenSocket listener;
80 EtherTap *tap;
81 int port;
82
83 public:
84 TapListener(EtherTap *t, int p)
85 : event(NULL), tap(t), port(p) {}
86 ~TapListener() { if (event) delete event; }
87
88 void accept();
89 void listen();
90 };
91
92 void
93 TapListener::listen()
94 {
95 while (!listener.listen(port, true)) {
96 DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port);
97 port++;
98 }
99
100 ccprintf(cerr, "Listening for tap connection on port %d\n", port);
101 event = new Event(this, listener.getfd(), POLLIN|POLLERR);
102 pollQueue.schedule(event);
103 }
104
105 void
106 TapListener::accept()
107 {
108 if (!listener.islistening())
109 panic("TapListener(accept): cannot accept if we're not listening!");
110
111 int sfd = listener.accept(true);
112 if (sfd != -1)
113 tap->attach(sfd);
114 }
115
116 /**
117 */
118 class TapEvent : public PollEvent
119 {
120 protected:
121 EtherTap *tap;
122
123 public:
124 TapEvent(EtherTap *_tap, int fd, int e)
125 : PollEvent(fd, e), tap(_tap) {}
126 virtual void process(int revent) { tap->process(revent); }
127 };
128
129 EtherTap::EtherTap(const Params *p)
130 : EtherObject(p), event(NULL), socket(-1), buflen(p->bufsz), dump(p->dump),
131 interface(NULL), txEvent(this)
132 {
133 if (ListenSocket::allDisabled())
134 fatal("All listeners are disabled! EtherTap can't work!");
135
136 buffer = new char[buflen];
137 listener = new TapListener(this, p->port);
138 listener->listen();
139 interface = new EtherTapInt(name() + ".interface", this);
140 }
141
142 EtherTap::~EtherTap()
143 {
144 if (event)
145 delete event;
146 if (buffer)
147 delete [] buffer;
148
149 delete listener;
150 }
151
152 void
153 EtherTap::attach(int fd)
154 {
155 if (socket != -1)
156 close(fd);
157
158 buffer_offset = 0;
159 data_len = 0;
160 socket = fd;
161 DPRINTF(Ethernet, "EtherTap attached\n");
162 event = new TapEvent(this, socket, POLLIN|POLLERR);
163 pollQueue.schedule(event);
164 }
165
166 void
167 EtherTap::detach()
168 {
169 DPRINTF(Ethernet, "EtherTap detached\n");
170 delete event;
171 event = 0;
172 close(socket);
173 socket = -1;
174 }
175
176 bool
177 EtherTap::recvPacket(EthPacketPtr packet)
178 {
179 if (dump)
180 dump->dump(packet);
181
182 DPRINTF(Ethernet, "EtherTap output len=%d\n", packet->length);
183 DDUMP(EthernetData, packet->data, packet->length);
184 uint32_t len = htonl(packet->length);
185 ssize_t ret = write(socket, &len, sizeof(len));
186 if (ret != sizeof(len))
187 return false;
188 ret = write(socket, packet->data, packet->length);
189 if (ret != packet->length)
190 return false;
191
192 interface->recvDone();
193
194 return true;
195 }
196
197 void
198 EtherTap::sendDone()
199 {}
200
201 void
202 EtherTap::process(int revent)
203 {
204 if (revent & POLLERR) {
205 detach();
206 return;
207 }
208
209 char *data = buffer + sizeof(uint32_t);
210 if (!(revent & POLLIN))
211 return;
212
213 if (buffer_offset < data_len + sizeof(uint32_t)) {
214 int len = read(socket, buffer + buffer_offset, buflen - buffer_offset);
215 if (len == 0) {
216 detach();
217 return;
218 }
219
220 buffer_offset += len;
221
222 if (data_len == 0)
223 data_len = ntohl(*(uint32_t *)buffer);
224
225 DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d "
226 "data_len=%d\n", len, buffer_offset, data_len);
227 }
228
229 while (data_len != 0 && buffer_offset >= data_len + sizeof(uint32_t)) {
230 EthPacketPtr packet;
231 packet = new EthPacketData(data_len);
232 packet->length = data_len;
233 memcpy(packet->data, data, data_len);
234
235 buffer_offset -= data_len + sizeof(uint32_t);
236 assert(buffer_offset >= 0);
237 if (buffer_offset > 0) {
238 memmove(buffer, data + data_len, buffer_offset);
239 data_len = ntohl(*(uint32_t *)buffer);
240 } else
241 data_len = 0;
242
243 DPRINTF(Ethernet, "EtherTap input len=%d\n", packet->length);
244 DDUMP(EthernetData, packet->data, packet->length);
245 if (!interface->sendPacket(packet)) {
246 DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
247 packetBuffer.push(packet);
248 if (!txEvent.scheduled())
249 schedule(txEvent, curTick + retryTime);
250 } else if (dump) {
251 dump->dump(packet);
252 }
253 }
254 }
255
256 void
257 EtherTap::retransmit()
258 {
259 if (packetBuffer.empty())
260 return;
261
262 EthPacketPtr packet = packetBuffer.front();
263 if (interface->sendPacket(packet)) {
264 if (dump)
265 dump->dump(packet);
266 DPRINTF(Ethernet, "EtherTap retransmit\n");
267 packetBuffer.front() = NULL;
268 packetBuffer.pop();
269 }
270
271 if (!packetBuffer.empty() && !txEvent.scheduled())
272 schedule(txEvent, curTick + retryTime);
273 }
274
275 EtherInt*
276 EtherTap::getEthPort(const std::string &if_name, int idx)
277 {
278 if (if_name == "tap") {
279 if (interface->getPeer())
280 panic("Interface already connected to\n");
281 return interface;
282 }
283 return NULL;
284 }
285
286
287 //=====================================================================
288
289 void
290 EtherTap::serialize(ostream &os)
291 {
292 SERIALIZE_SCALAR(socket);
293 SERIALIZE_SCALAR(buflen);
294 uint8_t *buffer = (uint8_t *)this->buffer;
295 SERIALIZE_ARRAY(buffer, buflen);
296 SERIALIZE_SCALAR(buffer_offset);
297 SERIALIZE_SCALAR(data_len);
298
299 bool tapevent_present = false;
300 if (event) {
301 tapevent_present = true;
302 SERIALIZE_SCALAR(tapevent_present);
303 event->serialize(os);
304 }
305 else {
306 SERIALIZE_SCALAR(tapevent_present);
307 }
308 }
309
310 void
311 EtherTap::unserialize(Checkpoint *cp, const std::string &section)
312 {
313 UNSERIALIZE_SCALAR(socket);
314 UNSERIALIZE_SCALAR(buflen);
315 uint8_t *buffer = (uint8_t *)this->buffer;
316 UNSERIALIZE_ARRAY(buffer, buflen);
317 UNSERIALIZE_SCALAR(buffer_offset);
318 UNSERIALIZE_SCALAR(data_len);
319
320 bool tapevent_present;
321 UNSERIALIZE_SCALAR(tapevent_present);
322 if (tapevent_present) {
323 event = new TapEvent(this, socket, POLLIN|POLLERR);
324
325 event->unserialize(cp,section);
326
327 if (event->queued()) {
328 pollQueue.schedule(event);
329 }
330 }
331 }
332
333 //=====================================================================
334
335 EtherTap *
336 EtherTapParams::create()
337 {
338 return new EtherTap(this);
339 }