vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / network / devices / ethernet / ethernet.cpp
blob0e5941a9e69b39ea93a071f93804282df11d997b
1 /*
2 * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 */
10 #include <ethernet.h>
12 #include <ether_driver.h>
13 #include <net_buffer.h>
14 #include <net_device.h>
15 #include <net_stack.h>
17 #include <lock.h>
18 #include <util/AutoLock.h>
19 #include <util/DoublyLinkedList.h>
21 #include <KernelExport.h>
23 #include <errno.h>
24 #include <net/if.h>
25 #include <net/if_dl.h>
26 #include <net/if_media.h>
27 #include <net/if_types.h>
28 #include <new>
29 #include <stdlib.h>
30 #include <string.h>
33 struct ethernet_device : net_device, DoublyLinkedListLinkImpl<ethernet_device> {
34 int fd;
35 uint32 frame_size;
38 static const bigtime_t kLinkCheckInterval = 1000000;
39 // 1 second
41 net_buffer_module_info *gBufferModule;
42 static net_stack_module_info *sStackModule;
44 static mutex sListLock;
45 static DoublyLinkedList<ethernet_device> sCheckList;
46 static sem_id sLinkChangeSemaphore;
47 static thread_id sLinkCheckerThread;
50 static status_t
51 update_link_state(ethernet_device *device, bool notify = true)
53 ether_link_state state;
54 if (ioctl(device->fd, ETHER_GET_LINK_STATE, &state,
55 sizeof(ether_link_state)) < 0) {
56 // This device does not support retrieving the link
57 return B_NOT_SUPPORTED;
60 state.media |= IFM_ETHER;
61 // make sure the media type is returned correctly
63 if (device->media != state.media
64 || device->link_quality != state.quality
65 || device->link_speed != state.speed) {
66 device->media = state.media;
67 device->link_quality = state.quality;
68 device->link_speed = state.speed;
70 if (device->media & IFM_ACTIVE)
71 device->flags |= IFF_LINK;
72 else
73 device->flags &= ~IFF_LINK;
75 dprintf("%s: media change, media 0x%0x quality %u speed %u\n",
76 device->name, (unsigned int)device->media,
77 (unsigned int)device->link_quality,
78 (unsigned int)device->link_speed);
80 if (notify)
81 sStackModule->device_link_changed(device);
84 return B_OK;
88 static status_t
89 ethernet_link_checker(void *)
91 while (true) {
92 status_t status = acquire_sem_etc(sLinkChangeSemaphore, 1,
93 B_RELATIVE_TIMEOUT, kLinkCheckInterval);
94 if (status == B_BAD_SEM_ID)
95 break;
97 MutexLocker _(sListLock);
99 if (sCheckList.IsEmpty())
100 break;
102 // check link state of all existing devices
104 DoublyLinkedList<ethernet_device>::Iterator iterator
105 = sCheckList.GetIterator();
106 while (iterator.HasNext()) {
107 update_link_state(iterator.Next());
111 return B_OK;
115 // #pragma mark -
118 status_t
119 ethernet_init(const char *name, net_device **_device)
121 // Make sure this is a device in /dev/net, but not the
122 // networking (userland) stack driver.
123 // Also make sure the user didn't pass a path like
124 // /dev/net/../etc.
125 if (strncmp(name, "/dev/net/", 9)
126 || !strcmp(name, "/dev/net/userland_server")
127 || strstr(name, "..") != NULL)
128 return B_BAD_VALUE;
130 if (access(name, F_OK) != 0)
131 return errno;
133 status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule);
134 if (status < B_OK)
135 return status;
137 ethernet_device *device = new (std::nothrow) ethernet_device;
138 if (device == NULL) {
139 put_module(NET_BUFFER_MODULE_NAME);
140 return B_NO_MEMORY;
143 memset(device, 0, sizeof(ethernet_device));
145 strcpy(device->name, name);
146 device->flags = IFF_BROADCAST | IFF_LINK;
147 device->type = IFT_ETHER;
148 device->mtu = 1500;
149 device->media = IFM_ACTIVE | IFM_ETHER;
150 device->header_length = ETHER_HEADER_LENGTH;
151 device->fd = -1;
153 *_device = device;
154 return B_OK;
158 status_t
159 ethernet_uninit(net_device *device)
161 put_module(NET_BUFFER_MODULE_NAME);
162 delete device;
164 return B_OK;
168 status_t
169 ethernet_up(net_device *_device)
171 ethernet_device *device = (ethernet_device *)_device;
173 device->fd = open(device->name, O_RDWR);
174 if (device->fd < 0)
175 return errno;
177 uint64 dummy;
178 if (ioctl(device->fd, ETHER_INIT, &dummy, sizeof(dummy)) < 0)
179 goto err;
181 if (ioctl(device->fd, ETHER_GETADDR, device->address.data, ETHER_ADDRESS_LENGTH) < 0)
182 goto err;
184 if (ioctl(device->fd, ETHER_GETFRAMESIZE, &device->frame_size, sizeof(uint32)) < 0) {
185 // this call is obviously optional
186 device->frame_size = ETHER_MAX_FRAME_SIZE;
189 if (update_link_state(device, false) == B_OK) {
190 // device supports retrieval of the link state
192 // Set the change notification semaphore; doesn't matter
193 // if this is supported by the device or not
194 ioctl(device->fd, ETHER_SET_LINK_STATE_SEM, &sLinkChangeSemaphore,
195 sizeof(sem_id));
197 MutexLocker _(&sListLock);
199 if (sCheckList.IsEmpty()) {
200 // start thread
201 sLinkCheckerThread = spawn_kernel_thread(ethernet_link_checker,
202 "ethernet link state checker", B_LOW_PRIORITY, NULL);
203 if (sLinkCheckerThread >= B_OK)
204 resume_thread(sLinkCheckerThread);
207 sCheckList.Add(device);
210 device->address.length = ETHER_ADDRESS_LENGTH;
211 device->mtu = device->frame_size - device->header_length;
212 return B_OK;
214 err:
215 close(device->fd);
216 device->fd = -1;
217 return errno;
221 void
222 ethernet_down(net_device *_device)
224 ethernet_device *device = (ethernet_device *)_device;
226 MutexLocker _(sListLock);
228 // if the device is still part of the list, remove it
229 if (device->GetDoublyLinkedListLink()->next != NULL
230 || device->GetDoublyLinkedListLink()->previous != NULL
231 || device == sCheckList.Head())
232 sCheckList.Remove(device);
234 close(device->fd);
235 device->fd = -1;
239 status_t
240 ethernet_control(net_device *_device, int32 op, void *argument,
241 size_t length)
243 ethernet_device *device = (ethernet_device *)_device;
244 return ioctl(device->fd, op, argument, length);
248 status_t
249 ethernet_send_data(net_device *_device, net_buffer *buffer)
251 ethernet_device *device = (ethernet_device *)_device;
253 //dprintf("try to send ethernet packet of %lu bytes (flags %ld):\n", buffer->size, buffer->flags);
254 if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH)
255 return B_BAD_VALUE;
257 net_buffer *allocated = NULL;
258 net_buffer *original = buffer;
260 if (gBufferModule->count_iovecs(buffer) > 1) {
261 // TODO: for now, create a new buffer containing the data
262 buffer = gBufferModule->duplicate(original);
263 if (buffer == NULL)
264 return ENOBUFS;
266 allocated = buffer;
268 if (gBufferModule->count_iovecs(buffer) > 1) {
269 dprintf("scattered I/O is not yet supported by ethernet device.\n");
270 gBufferModule->free(buffer);
271 device->stats.send.errors++;
272 return B_NOT_SUPPORTED;
276 struct iovec iovec;
277 gBufferModule->get_iovecs(buffer, &iovec, 1);
279 //dump_block((const char *)iovec.iov_base, buffer->size, " ");
280 ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len);
281 //dprintf("sent: %ld\n", bytesWritten);
283 if (bytesWritten < 0) {
284 device->stats.send.errors++;
285 if (allocated)
286 gBufferModule->free(allocated);
287 return errno;
290 device->stats.send.packets++;
291 device->stats.send.bytes += bytesWritten;
293 gBufferModule->free(original);
294 if (allocated)
295 gBufferModule->free(allocated);
296 return B_OK;
300 status_t
301 ethernet_receive_data(net_device *_device, net_buffer **_buffer)
303 ethernet_device *device = (ethernet_device *)_device;
305 if (device->fd == -1)
306 return B_FILE_ERROR;
308 // TODO: better header space
309 net_buffer *buffer = gBufferModule->create(256);
310 if (buffer == NULL)
311 return ENOBUFS;
313 // TODO: this only works for standard ethernet frames - we need iovecs
314 // for jumbo frame support (or a separate read buffer)!
315 // It would be even nicer to get net_buffers from the ethernet driver
316 // directly.
318 ssize_t bytesRead;
319 void *data;
321 status_t status = gBufferModule->append_size(buffer, device->frame_size, &data);
322 if (status == B_OK && data == NULL) {
323 dprintf("scattered I/O is not yet supported by ethernet device.\n");
324 status = B_NOT_SUPPORTED;
326 if (status < B_OK)
327 goto err;
329 bytesRead = read(device->fd, data, device->frame_size);
330 if (bytesRead < 0) {
331 device->stats.receive.errors++;
332 status = errno;
333 goto err;
335 //dump_block((const char *)data, bytesRead, "rcv: ");
337 status = gBufferModule->trim(buffer, bytesRead);
338 if (status < B_OK) {
339 device->stats.receive.dropped++;
340 goto err;
343 device->stats.receive.bytes += bytesRead;
344 device->stats.receive.packets++;
346 *_buffer = buffer;
347 return B_OK;
349 err:
350 gBufferModule->free(buffer);
351 return status;
355 status_t
356 ethernet_set_mtu(net_device *_device, size_t mtu)
358 ethernet_device *device = (ethernet_device *)_device;
360 if (mtu > device->frame_size - ETHER_HEADER_LENGTH
361 || mtu <= ETHER_HEADER_LENGTH + 10)
362 return B_BAD_VALUE;
364 device->mtu = mtu;
365 return B_OK;
369 status_t
370 ethernet_set_promiscuous(net_device *_device, bool promiscuous)
372 ethernet_device *device = (ethernet_device *)_device;
374 int32 value = (int32)promiscuous;
375 if (ioctl(device->fd, ETHER_SETPROMISC, &value, sizeof(value)) < 0)
376 return B_NOT_SUPPORTED;
378 return B_OK;
382 status_t
383 ethernet_set_media(net_device *device, uint32 media)
385 return B_NOT_SUPPORTED;
389 status_t
390 ethernet_add_multicast(struct net_device *_device, const sockaddr *_address)
392 ethernet_device *device = (ethernet_device *)_device;
394 if (_address->sa_family != AF_LINK)
395 return B_BAD_VALUE;
397 const sockaddr_dl *address = (const sockaddr_dl *)_address;
398 if (address->sdl_type != IFT_ETHER)
399 return B_BAD_VALUE;
401 return ioctl(device->fd, ETHER_ADDMULTI, LLADDR(address), 6);
405 status_t
406 ethernet_remove_multicast(struct net_device *_device, const sockaddr *_address)
408 ethernet_device *device = (ethernet_device *)_device;
410 if (_address->sa_family != AF_LINK)
411 return B_BAD_VALUE;
413 const sockaddr_dl *address = (const sockaddr_dl *)_address;
414 if (address->sdl_type != IFT_ETHER)
415 return B_BAD_VALUE;
417 return ioctl(device->fd, ETHER_REMMULTI, LLADDR(address), 6);
421 static status_t
422 ethernet_std_ops(int32 op, ...)
424 switch (op) {
425 case B_MODULE_INIT:
427 status_t status = get_module(NET_STACK_MODULE_NAME,
428 (module_info **)&sStackModule);
429 if (status < B_OK)
430 return status;
432 new (&sCheckList) DoublyLinkedList<ethernet_device>;
433 // static C++ objects are not initialized in the module startup
435 sLinkCheckerThread = -1;
437 sLinkChangeSemaphore = create_sem(0, "ethernet link change");
438 if (sLinkChangeSemaphore < B_OK) {
439 put_module(NET_STACK_MODULE_NAME);
440 return sLinkChangeSemaphore;
443 mutex_init(&sListLock, "ethernet devices");
445 return B_OK;
448 case B_MODULE_UNINIT:
450 delete_sem(sLinkChangeSemaphore);
452 status_t status;
453 wait_for_thread(sLinkCheckerThread, &status);
455 mutex_destroy(&sListLock);
456 put_module(NET_STACK_MODULE_NAME);
457 return B_OK;
460 default:
461 return B_ERROR;
466 net_device_module_info sEthernetModule = {
468 "network/devices/ethernet/v1",
470 ethernet_std_ops
472 ethernet_init,
473 ethernet_uninit,
474 ethernet_up,
475 ethernet_down,
476 ethernet_control,
477 ethernet_send_data,
478 ethernet_receive_data,
479 ethernet_set_mtu,
480 ethernet_set_promiscuous,
481 ethernet_set_media,
482 ethernet_add_multicast,
483 ethernet_remove_multicast,
486 module_info *modules[] = {
487 (module_info *)&sEthernetModule,
488 NULL