[21580dd] | 1 | /*
|
---|
| 2 | * Copyright (c) 2009 Lukas Mejdrech
|
---|
[609243f4] | 3 | * Copyright (c) 2011 Radim Vansa
|
---|
[21580dd] | 4 | * All rights reserved.
|
---|
| 5 | *
|
---|
| 6 | * Redistribution and use in source and binary forms, with or without
|
---|
| 7 | * modification, are permitted provided that the following conditions
|
---|
| 8 | * are met:
|
---|
| 9 | *
|
---|
| 10 | * - Redistributions of source code must retain the above copyright
|
---|
| 11 | * notice, this list of conditions and the following disclaimer.
|
---|
| 12 | * - Redistributions in binary form must reproduce the above copyright
|
---|
| 13 | * notice, this list of conditions and the following disclaimer in the
|
---|
| 14 | * documentation and/or other materials provided with the distribution.
|
---|
| 15 | * - The name of the author may not be used to endorse or promote products
|
---|
| 16 | * derived from this software without specific prior written permission.
|
---|
| 17 | *
|
---|
| 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
---|
| 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
---|
| 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
---|
| 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
---|
| 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
---|
| 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
| 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
| 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
| 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
---|
| 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
| 28 | */
|
---|
| 29 |
|
---|
| 30 | /** @addtogroup eth
|
---|
| 31 | * @{
|
---|
| 32 | */
|
---|
| 33 |
|
---|
| 34 | /** @file
|
---|
| 35 | * Ethernet module implementation.
|
---|
| 36 | * @see eth.h
|
---|
| 37 | */
|
---|
| 38 |
|
---|
[609243f4] | 39 | #include <assert.h>
|
---|
[21580dd] | 40 | #include <async.h>
|
---|
| 41 | #include <malloc.h>
|
---|
| 42 | #include <mem.h>
|
---|
| 43 | #include <stdio.h>
|
---|
[2687bdb] | 44 | #include <byteorder.h>
|
---|
[19f857a] | 45 | #include <str.h>
|
---|
[4ef32e0c] | 46 | #include <errno.h>
|
---|
[fe8dfa6] | 47 | #include <ipc/nil.h>
|
---|
[514ee46] | 48 | #include <ipc/net.h>
|
---|
[21580dd] | 49 | #include <ipc/services.h>
|
---|
[77a69ea] | 50 | #include <loc.h>
|
---|
[c7a8442] | 51 | #include <net/modules.h>
|
---|
[849ed54] | 52 | #include <net_checksum.h>
|
---|
| 53 | #include <ethernet_lsap.h>
|
---|
| 54 | #include <ethernet_protocols.h>
|
---|
| 55 | #include <protocol_map.h>
|
---|
[e526f08] | 56 | #include <net/device.h>
|
---|
[849ed54] | 57 | #include <net_interface.h>
|
---|
[797b704] | 58 | #include <il_remote.h>
|
---|
[849ed54] | 59 | #include <adt/measured_strings.h>
|
---|
[0a866eeb] | 60 | #include <packet_client.h>
|
---|
[14f1db0] | 61 | #include <packet_remote.h>
|
---|
[609243f4] | 62 | #include <device/nic.h>
|
---|
[fe8dfa6] | 63 | #include <nil_skel.h>
|
---|
[21580dd] | 64 | #include "eth.h"
|
---|
| 65 |
|
---|
[6067284] | 66 | /** The module name. */
|
---|
[24ab58b3] | 67 | #define NAME "eth"
|
---|
[849ed54] | 68 |
|
---|
[6067284] | 69 | /** Reserved packet prefix length. */
|
---|
| 70 | #define ETH_PREFIX \
|
---|
| 71 | (sizeof(eth_header_t) + sizeof(eth_header_lsap_t) + \
|
---|
[fe8dfa6] | 72 | sizeof(eth_header_snap_t))
|
---|
[21580dd] | 73 |
|
---|
[6067284] | 74 | /** Reserved packet suffix length. */
|
---|
[fe8dfa6] | 75 | #define ETH_SUFFIX (sizeof(eth_fcs_t))
|
---|
[21580dd] | 76 |
|
---|
[6067284] | 77 | /** Maximum packet content length. */
|
---|
[fe8dfa6] | 78 | #define ETH_MAX_CONTENT 1500u
|
---|
[21580dd] | 79 |
|
---|
[6067284] | 80 | /** Minimum packet content length. */
|
---|
[fe8dfa6] | 81 | #define ETH_MIN_CONTENT 46u
|
---|
[21580dd] | 82 |
|
---|
[6067284] | 83 | /** Maximum tagged packet content length. */
|
---|
| 84 | #define ETH_MAX_TAGGED_CONTENT(flags) \
|
---|
| 85 | (ETH_MAX_CONTENT - \
|
---|
[fe8dfa6] | 86 | ((IS_8023_2_LSAP(flags) || IS_8023_2_SNAP(flags)) ? \
|
---|
| 87 | sizeof(eth_header_lsap_t) : 0) - \
|
---|
| 88 | (IS_8023_2_SNAP(flags) ? sizeof(eth_header_snap_t) : 0))
|
---|
[6067284] | 89 |
|
---|
| 90 | /** Minimum tagged packet content length. */
|
---|
| 91 | #define ETH_MIN_TAGGED_CONTENT(flags) \
|
---|
| 92 | (ETH_MIN_CONTENT - \
|
---|
[fe8dfa6] | 93 | ((IS_8023_2_LSAP(flags) || IS_8023_2_SNAP(flags)) ? \
|
---|
| 94 | sizeof(eth_header_lsap_t) : 0) - \
|
---|
| 95 | (IS_8023_2_SNAP(flags) ? sizeof(eth_header_snap_t) : 0))
|
---|
[6067284] | 96 |
|
---|
| 97 | /** Dummy flag shift value. */
|
---|
[fe8dfa6] | 98 | #define ETH_DUMMY_SHIFT 0
|
---|
[21580dd] | 99 |
|
---|
[6067284] | 100 | /** Mode flag shift value. */
|
---|
[fe8dfa6] | 101 | #define ETH_MODE_SHIFT 1
|
---|
[21580dd] | 102 |
|
---|
| 103 | /** Dummy device flag.
|
---|
[6067284] | 104 | * Preamble and FCS are mandatory part of the packets.
|
---|
[21580dd] | 105 | */
|
---|
[fe8dfa6] | 106 | #define ETH_DUMMY (1 << ETH_DUMMY_SHIFT)
|
---|
[21580dd] | 107 |
|
---|
| 108 | /** Returns the dummy flag.
|
---|
[6067284] | 109 | * @see ETH_DUMMY
|
---|
[21580dd] | 110 | */
|
---|
[fe8dfa6] | 111 | #define IS_DUMMY(flags) ((flags) & ETH_DUMMY)
|
---|
[21580dd] | 112 |
|
---|
| 113 | /** Device mode flags.
|
---|
[6067284] | 114 | * @see ETH_DIX
|
---|
| 115 | * @see ETH_8023_2_LSAP
|
---|
| 116 | * @see ETH_8023_2_SNAP
|
---|
[21580dd] | 117 | */
|
---|
[fe8dfa6] | 118 | #define ETH_MODE_MASK (3 << ETH_MODE_SHIFT)
|
---|
[21580dd] | 119 |
|
---|
[6067284] | 120 | /** DIX Ethernet mode flag. */
|
---|
[fe8dfa6] | 121 | #define ETH_DIX (1 << ETH_MODE_SHIFT)
|
---|
[21580dd] | 122 |
|
---|
[fe8dfa6] | 123 | /** Return whether the DIX Ethernet mode flag is set.
|
---|
[6067284] | 124 | *
|
---|
[fe8dfa6] | 125 | * @param[in] flags Ethernet flags.
|
---|
[6067284] | 126 | * @see ETH_DIX
|
---|
[fe8dfa6] | 127 | *
|
---|
[21580dd] | 128 | */
|
---|
[fe8dfa6] | 129 | #define IS_DIX(flags) (((flags) & ETH_MODE_MASK) == ETH_DIX)
|
---|
[21580dd] | 130 |
|
---|
[6067284] | 131 | /** 802.3 + 802.2 + LSAP mode flag. */
|
---|
[fe8dfa6] | 132 | #define ETH_8023_2_LSAP (2 << ETH_MODE_SHIFT)
|
---|
[21580dd] | 133 |
|
---|
[fe8dfa6] | 134 | /** Return whether the 802.3 + 802.2 + LSAP mode flag is set.
|
---|
[6067284] | 135 | *
|
---|
[fe8dfa6] | 136 | * @param[in] flags Ethernet flags.
|
---|
[6067284] | 137 | * @see ETH_8023_2_LSAP
|
---|
[fe8dfa6] | 138 | *
|
---|
[21580dd] | 139 | */
|
---|
[fe8dfa6] | 140 | #define IS_8023_2_LSAP(flags) (((flags) & ETH_MODE_MASK) == ETH_8023_2_LSAP)
|
---|
[21580dd] | 141 |
|
---|
[6067284] | 142 | /** 802.3 + 802.2 + LSAP + SNAP mode flag. */
|
---|
[fe8dfa6] | 143 | #define ETH_8023_2_SNAP (3 << ETH_MODE_SHIFT)
|
---|
[21580dd] | 144 |
|
---|
[fe8dfa6] | 145 | /** Return whether the 802.3 + 802.2 + LSAP + SNAP mode flag is set.
|
---|
[6067284] | 146 | *
|
---|
[fe8dfa6] | 147 | * @param[in] flags Ethernet flags.
|
---|
[6067284] | 148 | * @see ETH_8023_2_SNAP
|
---|
[fe8dfa6] | 149 | *
|
---|
[21580dd] | 150 | */
|
---|
[fe8dfa6] | 151 | #define IS_8023_2_SNAP(flags) (((flags) & ETH_MODE_MASK) == ETH_8023_2_SNAP)
|
---|
[21580dd] | 152 |
|
---|
| 153 | /** Type definition of the ethernet address type.
|
---|
[6067284] | 154 | * @see eth_addr_type
|
---|
[21580dd] | 155 | */
|
---|
[6067284] | 156 | typedef enum eth_addr_type eth_addr_type_t;
|
---|
[21580dd] | 157 |
|
---|
[6067284] | 158 | /** Ethernet address type. */
|
---|
| 159 | enum eth_addr_type {
|
---|
| 160 | /** Local address. */
|
---|
[21580dd] | 161 | ETH_LOCAL_ADDR,
|
---|
[6067284] | 162 | /** Broadcast address. */
|
---|
[21580dd] | 163 | ETH_BROADCAST_ADDR
|
---|
| 164 | };
|
---|
| 165 |
|
---|
[6067284] | 166 | /** Ethernet module global data. */
|
---|
| 167 | eth_globals_t eth_globals;
|
---|
[21580dd] | 168 |
|
---|
[6067284] | 169 | DEVICE_MAP_IMPLEMENT(eth_devices, eth_device_t);
|
---|
| 170 | INT_MAP_IMPLEMENT(eth_protos, eth_proto_t);
|
---|
[21580dd] | 171 |
|
---|
[8d7ec69d] | 172 | static void eth_nic_cb_connection(ipc_callid_t iid, ipc_call_t *icall,
|
---|
| 173 | void *arg);
|
---|
| 174 |
|
---|
| 175 | static int eth_device_state(nic_device_id_t device_id, sysarg_t state)
|
---|
[6067284] | 176 | {
|
---|
[aadf01e] | 177 | int index;
|
---|
[4e5c7ba] | 178 | eth_proto_t *proto;
|
---|
[21580dd] | 179 |
|
---|
[aadf01e] | 180 | fibril_rwlock_read_lock(ð_globals.protos_lock);
|
---|
[6067284] | 181 | for (index = eth_protos_count(ð_globals.protos) - 1; index >= 0;
|
---|
| 182 | index--) {
|
---|
[aadf01e] | 183 | proto = eth_protos_get_index(ð_globals.protos, index);
|
---|
[6b82009] | 184 | if ((proto) && (proto->sess)) {
|
---|
| 185 | il_device_state_msg(proto->sess, device_id, state,
|
---|
[6067284] | 186 | proto->service);
|
---|
[aadf01e] | 187 | }
|
---|
[21580dd] | 188 | }
|
---|
[aadf01e] | 189 | fibril_rwlock_read_unlock(ð_globals.protos_lock);
|
---|
[6067284] | 190 |
|
---|
[21580dd] | 191 | return EOK;
|
---|
| 192 | }
|
---|
| 193 |
|
---|
[6b82009] | 194 | int nil_initialize(async_sess_t *sess)
|
---|
[6067284] | 195 | {
|
---|
[4ef32e0c] | 196 | int rc;
|
---|
[21580dd] | 197 |
|
---|
[aadf01e] | 198 | fibril_rwlock_initialize(ð_globals.devices_lock);
|
---|
| 199 | fibril_rwlock_initialize(ð_globals.protos_lock);
|
---|
[6067284] | 200 |
|
---|
[aadf01e] | 201 | fibril_rwlock_write_lock(ð_globals.devices_lock);
|
---|
| 202 | fibril_rwlock_write_lock(ð_globals.protos_lock);
|
---|
[4ee7364] | 203 |
|
---|
[6b82009] | 204 | eth_globals.net_sess = sess;
|
---|
[609243f4] | 205 | memcpy(eth_globals.broadcast_addr, "\xFF\xFF\xFF\xFF\xFF\xFF",
|
---|
| 206 | ETH_ADDR);
|
---|
[fb04cba8] | 207 |
|
---|
[4ef32e0c] | 208 | rc = eth_devices_initialize(ð_globals.devices);
|
---|
| 209 | if (rc != EOK) {
|
---|
[0a3fbc7] | 210 | free(eth_globals.broadcast_addr);
|
---|
[6067284] | 211 | goto out;
|
---|
[0a3fbc7] | 212 | }
|
---|
[fb04cba8] | 213 |
|
---|
[4ef32e0c] | 214 | rc = eth_protos_initialize(ð_globals.protos);
|
---|
| 215 | if (rc != EOK) {
|
---|
[0a3fbc7] | 216 | free(eth_globals.broadcast_addr);
|
---|
[5fe7692] | 217 | eth_devices_destroy(ð_globals.devices, free);
|
---|
[0a3fbc7] | 218 | }
|
---|
[609243f4] | 219 |
|
---|
[6067284] | 220 | out:
|
---|
[aadf01e] | 221 | fibril_rwlock_write_unlock(ð_globals.protos_lock);
|
---|
| 222 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
[6067284] | 223 |
|
---|
[4ef32e0c] | 224 | return rc;
|
---|
[21580dd] | 225 | }
|
---|
| 226 |
|
---|
[609243f4] | 227 | /** Register new device or updates the MTU of an existing one.
|
---|
[6067284] | 228 | *
|
---|
[609243f4] | 229 | * Determine the device local hardware address.
|
---|
[6b82009] | 230 | *
|
---|
[609243f4] | 231 | * @param[in] device_id New device identifier.
|
---|
[77a69ea] | 232 | * @param[in] sid NIC service ID.
|
---|
[609243f4] | 233 | * @param[in] mtu Device maximum transmission unit.
|
---|
[6067284] | 234 | *
|
---|
[609243f4] | 235 | * @return EOK on success.
|
---|
| 236 | * @return EEXIST if the device with the different service exists.
|
---|
| 237 | * @return ENOMEM if there is not enough memory left.
|
---|
[6067284] | 238 | *
|
---|
| 239 | */
|
---|
[77a69ea] | 240 | static int eth_device_message(nic_device_id_t device_id, service_id_t sid,
|
---|
[fb04cba8] | 241 | size_t mtu)
|
---|
[6067284] | 242 | {
|
---|
[4e5c7ba] | 243 | eth_device_t *device;
|
---|
[aadf01e] | 244 | int index;
|
---|
[6067284] | 245 | measured_string_t names[2] = {
|
---|
| 246 | {
|
---|
[61bfc370] | 247 | (uint8_t *) "ETH_MODE",
|
---|
[6067284] | 248 | 8
|
---|
| 249 | },
|
---|
| 250 | {
|
---|
[61bfc370] | 251 | (uint8_t *) "ETH_DUMMY",
|
---|
[6067284] | 252 | 9
|
---|
| 253 | }
|
---|
| 254 | };
|
---|
[4eca056] | 255 | measured_string_t *configuration;
|
---|
[aadf01e] | 256 | size_t count = sizeof(names) / sizeof(measured_string_t);
|
---|
[61bfc370] | 257 | uint8_t *data;
|
---|
[4e5c7ba] | 258 | eth_proto_t *proto;
|
---|
[4ef32e0c] | 259 | int rc;
|
---|
[21580dd] | 260 |
|
---|
[aadf01e] | 261 | fibril_rwlock_write_lock(ð_globals.devices_lock);
|
---|
[fb04cba8] | 262 | /* An existing device? */
|
---|
[aadf01e] | 263 | device = eth_devices_find(ð_globals.devices, device_id);
|
---|
[6067284] | 264 | if (device) {
|
---|
[77a69ea] | 265 | if (device->sid != sid) {
|
---|
[aadf01e] | 266 | printf("Device %d already exists\n", device->device_id);
|
---|
| 267 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
[21580dd] | 268 | return EEXIST;
|
---|
[aadf01e] | 269 | }
|
---|
[6067284] | 270 |
|
---|
[fb04cba8] | 271 | /* Update mtu */
|
---|
[6067284] | 272 | if ((mtu > 0) && (mtu <= ETH_MAX_TAGGED_CONTENT(device->flags)))
|
---|
[21580dd] | 273 | device->mtu = mtu;
|
---|
[6067284] | 274 | else
|
---|
| 275 | device->mtu = ETH_MAX_TAGGED_CONTENT(device->flags);
|
---|
| 276 |
|
---|
[7e752b2] | 277 | printf("Device %d already exists:\tMTU\t= %zu\n",
|
---|
[6067284] | 278 | device->device_id, device->mtu);
|
---|
| 279 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
| 280 |
|
---|
[fb04cba8] | 281 | /* Notify all upper layer modules */
|
---|
[6067284] | 282 | fibril_rwlock_read_lock(ð_globals.protos_lock);
|
---|
| 283 | for (index = 0; index < eth_protos_count(ð_globals.protos);
|
---|
| 284 | index++) {
|
---|
| 285 | proto = eth_protos_get_index(ð_globals.protos,
|
---|
| 286 | index);
|
---|
[6b82009] | 287 | if (proto->sess) {
|
---|
| 288 | il_mtu_changed_msg(proto->sess,
|
---|
[6067284] | 289 | device->device_id, device->mtu,
|
---|
| 290 | proto->service);
|
---|
[21580dd] | 291 | }
|
---|
| 292 | }
|
---|
[fb04cba8] | 293 |
|
---|
[6067284] | 294 | fibril_rwlock_read_unlock(ð_globals.protos_lock);
|
---|
| 295 | return EOK;
|
---|
| 296 | }
|
---|
| 297 |
|
---|
[fb04cba8] | 298 | /* Create a new device */
|
---|
[4e5c7ba] | 299 | device = (eth_device_t *) malloc(sizeof(eth_device_t));
|
---|
[6067284] | 300 | if (!device)
|
---|
| 301 | return ENOMEM;
|
---|
| 302 |
|
---|
| 303 | device->device_id = device_id;
|
---|
[77a69ea] | 304 | device->sid = sid;
|
---|
[6067284] | 305 | device->flags = 0;
|
---|
| 306 | if ((mtu > 0) && (mtu <= ETH_MAX_TAGGED_CONTENT(device->flags)))
|
---|
| 307 | device->mtu = mtu;
|
---|
| 308 | else
|
---|
| 309 | device->mtu = ETH_MAX_TAGGED_CONTENT(device->flags);
|
---|
| 310 |
|
---|
| 311 | configuration = &names[0];
|
---|
[6b82009] | 312 | rc = net_get_device_conf_req(eth_globals.net_sess, device->device_id,
|
---|
[4ef32e0c] | 313 | &configuration, count, &data);
|
---|
| 314 | if (rc != EOK) {
|
---|
[6067284] | 315 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
| 316 | free(device);
|
---|
[4ef32e0c] | 317 | return rc;
|
---|
[6067284] | 318 | }
|
---|
[fb04cba8] | 319 |
|
---|
[6067284] | 320 | if (configuration) {
|
---|
[61bfc370] | 321 | if (!str_lcmp((char *) configuration[0].value, "DIX",
|
---|
[6067284] | 322 | configuration[0].length)) {
|
---|
| 323 | device->flags |= ETH_DIX;
|
---|
[61bfc370] | 324 | } else if(!str_lcmp((char *) configuration[0].value, "8023_2_LSAP",
|
---|
[6067284] | 325 | configuration[0].length)) {
|
---|
| 326 | device->flags |= ETH_8023_2_LSAP;
|
---|
| 327 | } else {
|
---|
| 328 | device->flags |= ETH_8023_2_SNAP;
|
---|
[21580dd] | 329 | }
|
---|
[6067284] | 330 |
|
---|
| 331 | if (configuration[1].value &&
|
---|
| 332 | (configuration[1].value[0] == 'y')) {
|
---|
| 333 | device->flags |= ETH_DUMMY;
|
---|
[21580dd] | 334 | }
|
---|
[6067284] | 335 | net_free_settings(configuration, data);
|
---|
| 336 | } else {
|
---|
| 337 | device->flags |= ETH_8023_2_SNAP;
|
---|
[21580dd] | 338 | }
|
---|
[6067284] | 339 |
|
---|
[fb04cba8] | 340 | /* Bind the device driver */
|
---|
[77a69ea] | 341 | device->sess = loc_service_connect(EXCHANGE_SERIALIZE, sid,
|
---|
[609243f4] | 342 | IPC_FLAG_BLOCKING);
|
---|
[6b82009] | 343 | if (device->sess == NULL) {
|
---|
[6067284] | 344 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
| 345 | free(device);
|
---|
[6b82009] | 346 | return ENOENT;
|
---|
[6067284] | 347 | }
|
---|
| 348 |
|
---|
[8d7ec69d] | 349 | rc = nic_callback_create(device->sess, device_id,
|
---|
| 350 | eth_nic_cb_connection, NULL);
|
---|
| 351 | if (rc != EOK) {
|
---|
| 352 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
| 353 | async_hangup(device->sess);
|
---|
| 354 | free(device);
|
---|
| 355 | return EIO;
|
---|
| 356 | }
|
---|
[609243f4] | 357 |
|
---|
[fb04cba8] | 358 | /* Get hardware address */
|
---|
[609243f4] | 359 | rc = nic_get_address(device->sess, &device->addr);
|
---|
[4ef32e0c] | 360 | if (rc != EOK) {
|
---|
[6067284] | 361 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
| 362 | free(device);
|
---|
[4ef32e0c] | 363 | return rc;
|
---|
[6067284] | 364 | }
|
---|
| 365 |
|
---|
[fb04cba8] | 366 | /* Add to the cache */
|
---|
[6067284] | 367 | index = eth_devices_add(ð_globals.devices, device->device_id,
|
---|
| 368 | device);
|
---|
| 369 | if (index < 0) {
|
---|
| 370 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
| 371 | free(device);
|
---|
| 372 | return index;
|
---|
| 373 | }
|
---|
| 374 |
|
---|
[77a69ea] | 375 | printf("%s: Device registered (id: %d, sid: %zu: mtu: %zu, "
|
---|
[609243f4] | 376 | "mac: " PRIMAC ", flags: 0x%x)\n", NAME,
|
---|
[77a69ea] | 377 | device->device_id, device->sid, device->mtu,
|
---|
[609243f4] | 378 | ARGSMAC(device->addr.address), device->flags);
|
---|
[6067284] | 379 |
|
---|
[aadf01e] | 380 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
[21580dd] | 381 | return EOK;
|
---|
| 382 | }
|
---|
| 383 |
|
---|
[6067284] | 384 | /** Processes the received packet and chooses the target registered module.
|
---|
| 385 | *
|
---|
| 386 | * @param[in] flags The device flags.
|
---|
| 387 | * @param[in] packet The packet.
|
---|
[1bfd3d3] | 388 | * @return The target registered module.
|
---|
| 389 | * @return NULL if the packet is not long enough.
|
---|
| 390 | * @return NULL if the packet is too long.
|
---|
| 391 | * @return NULL if the raw ethernet protocol is used.
|
---|
| 392 | * @return NULL if the dummy device FCS checksum is invalid.
|
---|
| 393 | * @return NULL if the packet address length is not big enough.
|
---|
[6067284] | 394 | */
|
---|
[46d4d9f] | 395 | static eth_proto_t *eth_process_packet(int flags, packet_t *packet)
|
---|
[6067284] | 396 | {
|
---|
[4e5c7ba] | 397 | eth_header_snap_t *header;
|
---|
[aadf01e] | 398 | size_t length;
|
---|
| 399 | eth_type_t type;
|
---|
| 400 | size_t prefix;
|
---|
| 401 | size_t suffix;
|
---|
[4e5c7ba] | 402 | eth_fcs_t *fcs;
|
---|
[4ef32e0c] | 403 | uint8_t *data;
|
---|
| 404 | int rc;
|
---|
[aadf01e] | 405 |
|
---|
| 406 | length = packet_get_data_length(packet);
|
---|
[6067284] | 407 |
|
---|
| 408 | if (IS_DUMMY(flags))
|
---|
[aadf01e] | 409 | packet_trim(packet, sizeof(eth_preamble_t), 0);
|
---|
[6067284] | 410 | if (length < sizeof(eth_header_t) + ETH_MIN_CONTENT +
|
---|
| 411 | (IS_DUMMY(flags) ? ETH_SUFFIX : 0))
|
---|
| 412 | return NULL;
|
---|
| 413 |
|
---|
[aadf01e] | 414 | data = packet_get_data(packet);
|
---|
[4e5c7ba] | 415 | header = (eth_header_snap_t *) data;
|
---|
[aadf01e] | 416 | type = ntohs(header->header.ethertype);
|
---|
[6067284] | 417 |
|
---|
| 418 | if (type >= ETH_MIN_PROTO) {
|
---|
[fb04cba8] | 419 | /* DIX Ethernet */
|
---|
[aadf01e] | 420 | prefix = sizeof(eth_header_t);
|
---|
[21580dd] | 421 | suffix = 0;
|
---|
[4e5c7ba] | 422 | fcs = (eth_fcs_t *) data + length - sizeof(eth_fcs_t);
|
---|
[aadf01e] | 423 | length -= sizeof(eth_fcs_t);
|
---|
[609243f4] | 424 | } else if (type <= ETH_MAX_CONTENT) {
|
---|
[fb04cba8] | 425 | /* Translate "LSAP" values */
|
---|
[6067284] | 426 | if ((header->lsap.dsap == ETH_LSAP_GLSAP) &&
|
---|
| 427 | (header->lsap.ssap == ETH_LSAP_GLSAP)) {
|
---|
[fb04cba8] | 428 | /* Raw packet -- discard */
|
---|
[21580dd] | 429 | return NULL;
|
---|
[609243f4] | 430 | } else if ((header->lsap.dsap == ETH_LSAP_SNAP) &&
|
---|
[6067284] | 431 | (header->lsap.ssap == ETH_LSAP_SNAP)) {
|
---|
[fb04cba8] | 432 | /*
|
---|
| 433 | * IEEE 802.3 + 802.2 + LSAP + SNAP
|
---|
| 434 | * organization code not supported
|
---|
| 435 | */
|
---|
[aadf01e] | 436 | type = ntohs(header->snap.ethertype);
|
---|
[609243f4] | 437 | prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t) +
|
---|
[6067284] | 438 | sizeof(eth_header_snap_t);
|
---|
| 439 | } else {
|
---|
[fb04cba8] | 440 | /* IEEE 802.3 + 802.2 LSAP */
|
---|
[aadf01e] | 441 | type = lsap_map(header->lsap.dsap);
|
---|
[609243f4] | 442 | prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t);
|
---|
[21580dd] | 443 | }
|
---|
[fb04cba8] | 444 |
|
---|
[6067284] | 445 | suffix = (type < ETH_MIN_CONTENT) ? ETH_MIN_CONTENT - type : 0U;
|
---|
[4e5c7ba] | 446 | fcs = (eth_fcs_t *) data + prefix + type + suffix;
|
---|
[21580dd] | 447 | suffix += length - prefix - type;
|
---|
| 448 | length = prefix + type + suffix;
|
---|
[6067284] | 449 | } else {
|
---|
[fb04cba8] | 450 | /* Invalid length/type, should not occur */
|
---|
[21580dd] | 451 | return NULL;
|
---|
| 452 | }
|
---|
[6067284] | 453 |
|
---|
| 454 | if (IS_DUMMY(flags)) {
|
---|
[4ef32e0c] | 455 | if (~compute_crc32(~0U, data, length * 8) != ntohl(*fcs))
|
---|
[21580dd] | 456 | return NULL;
|
---|
[aadf01e] | 457 | suffix += sizeof(eth_fcs_t);
|
---|
[21580dd] | 458 | }
|
---|
[6067284] | 459 |
|
---|
[4ef32e0c] | 460 | rc = packet_set_addr(packet, header->header.source_address,
|
---|
| 461 | header->header.destination_address, ETH_ADDR);
|
---|
| 462 | if (rc != EOK)
|
---|
| 463 | return NULL;
|
---|
| 464 |
|
---|
| 465 | rc = packet_trim(packet, prefix, suffix);
|
---|
| 466 | if (rc != EOK)
|
---|
[21580dd] | 467 | return NULL;
|
---|
[6067284] | 468 |
|
---|
[aadf01e] | 469 | return eth_protos_find(ð_globals.protos, type);
|
---|
[21580dd] | 470 | }
|
---|
| 471 |
|
---|
[609243f4] | 472 | int nil_received_msg_local(nic_device_id_t device_id, packet_t *packet)
|
---|
[6067284] | 473 | {
|
---|
[4e5c7ba] | 474 | eth_proto_t *proto;
|
---|
[46d4d9f] | 475 | packet_t *next;
|
---|
[4e5c7ba] | 476 | eth_device_t *device;
|
---|
[aadf01e] | 477 | int flags;
|
---|
[21580dd] | 478 |
|
---|
[aadf01e] | 479 | fibril_rwlock_read_lock(ð_globals.devices_lock);
|
---|
| 480 | device = eth_devices_find(ð_globals.devices, device_id);
|
---|
[6067284] | 481 | if (!device) {
|
---|
[aadf01e] | 482 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
[21580dd] | 483 | return ENOENT;
|
---|
| 484 | }
|
---|
[fb04cba8] | 485 |
|
---|
[21580dd] | 486 | flags = device->flags;
|
---|
[aadf01e] | 487 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
| 488 | fibril_rwlock_read_lock(ð_globals.protos_lock);
|
---|
[609243f4] | 489 |
|
---|
[6067284] | 490 | do {
|
---|
[aadf01e] | 491 | next = pq_detach(packet);
|
---|
| 492 | proto = eth_process_packet(flags, packet);
|
---|
[6067284] | 493 | if (proto) {
|
---|
[6b82009] | 494 | il_received_msg(proto->sess, device_id, packet,
|
---|
[6067284] | 495 | proto->service);
|
---|
| 496 | } else {
|
---|
[28a3e74] | 497 | /* Drop invalid/unknown */
|
---|
[6b82009] | 498 | pq_release_remote(eth_globals.net_sess,
|
---|
[6067284] | 499 | packet_get_id(packet));
|
---|
[21580dd] | 500 | }
|
---|
| 501 | packet = next;
|
---|
[609243f4] | 502 | } while (packet);
|
---|
[fb04cba8] | 503 |
|
---|
[aadf01e] | 504 | fibril_rwlock_read_unlock(ð_globals.protos_lock);
|
---|
[21580dd] | 505 | return EOK;
|
---|
| 506 | }
|
---|
| 507 |
|
---|
[6067284] | 508 | /** Returns the device packet dimensions for sending.
|
---|
| 509 | *
|
---|
| 510 | * @param[in] device_id The device identifier.
|
---|
| 511 | * @param[out] addr_len The minimum reserved address length.
|
---|
| 512 | * @param[out] prefix The minimum reserved prefix size.
|
---|
| 513 | * @param[out] content The maximum content size.
|
---|
| 514 | * @param[out] suffix The minimum reserved suffix size.
|
---|
[1bfd3d3] | 515 | * @return EOK on success.
|
---|
| 516 | * @return EBADMEM if either one of the parameters is NULL.
|
---|
| 517 | * @return ENOENT if there is no such device.
|
---|
[6067284] | 518 | */
|
---|
[609243f4] | 519 | static int eth_packet_space_message(nic_device_id_t device_id, size_t *addr_len,
|
---|
[6067284] | 520 | size_t *prefix, size_t *content, size_t *suffix)
|
---|
| 521 | {
|
---|
[4e5c7ba] | 522 | eth_device_t *device;
|
---|
[21580dd] | 523 |
|
---|
[6067284] | 524 | if (!addr_len || !prefix || !content || !suffix)
|
---|
[aadf01e] | 525 | return EBADMEM;
|
---|
[6067284] | 526 |
|
---|
[aadf01e] | 527 | fibril_rwlock_read_lock(ð_globals.devices_lock);
|
---|
| 528 | device = eth_devices_find(ð_globals.devices, device_id);
|
---|
[6067284] | 529 | if (!device) {
|
---|
[aadf01e] | 530 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
[21580dd] | 531 | return ENOENT;
|
---|
| 532 | }
|
---|
[fb04cba8] | 533 |
|
---|
[aadf01e] | 534 | *content = device->mtu;
|
---|
| 535 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
[6067284] | 536 |
|
---|
[aadf01e] | 537 | *addr_len = ETH_ADDR;
|
---|
| 538 | *prefix = ETH_PREFIX;
|
---|
| 539 | *suffix = ETH_MIN_CONTENT + ETH_SUFFIX;
|
---|
[fb04cba8] | 540 |
|
---|
[21580dd] | 541 | return EOK;
|
---|
| 542 | }
|
---|
| 543 |
|
---|
[609243f4] | 544 | /** Send the device hardware address.
|
---|
[6067284] | 545 | *
|
---|
| 546 | * @param[in] device_id The device identifier.
|
---|
| 547 | * @param[in] type Type of the desired address.
|
---|
[1bfd3d3] | 548 | * @return EOK on success.
|
---|
| 549 | * @return EBADMEM if the address parameter is NULL.
|
---|
| 550 | * @return ENOENT if there no such device.
|
---|
[6067284] | 551 | */
|
---|
[609243f4] | 552 | static int eth_addr_message(nic_device_id_t device_id, eth_addr_type_t type)
|
---|
[6067284] | 553 | {
|
---|
[609243f4] | 554 | eth_device_t *device = NULL;
|
---|
| 555 | uint8_t *address;
|
---|
| 556 | size_t max_len;
|
---|
| 557 | ipc_callid_t callid;
|
---|
| 558 |
|
---|
| 559 | if (type == ETH_BROADCAST_ADDR)
|
---|
| 560 | address = eth_globals.broadcast_addr;
|
---|
| 561 | else {
|
---|
[aadf01e] | 562 | fibril_rwlock_read_lock(ð_globals.devices_lock);
|
---|
| 563 | device = eth_devices_find(ð_globals.devices, device_id);
|
---|
[6067284] | 564 | if (!device) {
|
---|
[aadf01e] | 565 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
[21580dd] | 566 | return ENOENT;
|
---|
| 567 | }
|
---|
[609243f4] | 568 |
|
---|
| 569 | address = (uint8_t *) &device->addr.address;
|
---|
| 570 | }
|
---|
| 571 |
|
---|
| 572 | int rc = EOK;
|
---|
| 573 | if (!async_data_read_receive(&callid, &max_len)) {
|
---|
| 574 | rc = EREFUSED;
|
---|
| 575 | goto end;
|
---|
| 576 | }
|
---|
| 577 |
|
---|
| 578 | if (max_len < ETH_ADDR) {
|
---|
| 579 | async_data_read_finalize(callid, NULL, 0);
|
---|
| 580 | rc = ELIMIT;
|
---|
| 581 | goto end;
|
---|
[21580dd] | 582 | }
|
---|
[6067284] | 583 |
|
---|
[609243f4] | 584 | rc = async_data_read_finalize(callid, address, ETH_ADDR);
|
---|
| 585 | if (rc != EOK)
|
---|
| 586 | goto end;
|
---|
| 587 |
|
---|
| 588 | end:
|
---|
| 589 |
|
---|
| 590 | if (type == ETH_LOCAL_ADDR)
|
---|
| 591 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
| 592 |
|
---|
| 593 | return rc;
|
---|
[21580dd] | 594 | }
|
---|
| 595 |
|
---|
[6b82009] | 596 | /** Register receiving module service.
|
---|
[6067284] | 597 | *
|
---|
[6b82009] | 598 | * Pass received packets for this service.
|
---|
| 599 | *
|
---|
| 600 | * @param[in] service Module service.
|
---|
| 601 | * @param[in] sess Service session.
|
---|
| 602 | *
|
---|
| 603 | * @return EOK on success.
|
---|
| 604 | * @return ENOENT if the service is not known.
|
---|
| 605 | * @return ENOMEM if there is not enough memory left.
|
---|
[6067284] | 606 | *
|
---|
| 607 | */
|
---|
[6b82009] | 608 | static int eth_register_message(services_t service, async_sess_t *sess)
|
---|
[6067284] | 609 | {
|
---|
[4e5c7ba] | 610 | eth_proto_t *proto;
|
---|
[aadf01e] | 611 | int protocol;
|
---|
| 612 | int index;
|
---|
[21580dd] | 613 |
|
---|
[aadf01e] | 614 | protocol = protocol_map(SERVICE_ETHERNET, service);
|
---|
[6067284] | 615 | if (!protocol)
|
---|
[aadf01e] | 616 | return ENOENT;
|
---|
[6067284] | 617 |
|
---|
[aadf01e] | 618 | fibril_rwlock_write_lock(ð_globals.protos_lock);
|
---|
| 619 | proto = eth_protos_find(ð_globals.protos, protocol);
|
---|
[6067284] | 620 | if (proto) {
|
---|
[6b82009] | 621 | proto->sess = sess;
|
---|
[aadf01e] | 622 | fibril_rwlock_write_unlock(ð_globals.protos_lock);
|
---|
[21580dd] | 623 | return EOK;
|
---|
[6067284] | 624 | } else {
|
---|
[4e5c7ba] | 625 | proto = (eth_proto_t *) malloc(sizeof(eth_proto_t));
|
---|
[6067284] | 626 | if (!proto) {
|
---|
[aadf01e] | 627 | fibril_rwlock_write_unlock(ð_globals.protos_lock);
|
---|
[21580dd] | 628 | return ENOMEM;
|
---|
| 629 | }
|
---|
[fb04cba8] | 630 |
|
---|
[21580dd] | 631 | proto->service = service;
|
---|
| 632 | proto->protocol = protocol;
|
---|
[6b82009] | 633 | proto->sess = sess;
|
---|
[fb04cba8] | 634 |
|
---|
[aadf01e] | 635 | index = eth_protos_add(ð_globals.protos, protocol, proto);
|
---|
[6067284] | 636 | if (index < 0) {
|
---|
[aadf01e] | 637 | fibril_rwlock_write_unlock(ð_globals.protos_lock);
|
---|
| 638 | free(proto);
|
---|
[21580dd] | 639 | return index;
|
---|
| 640 | }
|
---|
| 641 | }
|
---|
[24ab58b3] | 642 |
|
---|
[00d7e1b] | 643 | printf("%s: Protocol registered (protocol: %d, service: %#x)\n",
|
---|
[6b82009] | 644 | NAME, proto->protocol, proto->service);
|
---|
[24ab58b3] | 645 |
|
---|
[aadf01e] | 646 | fibril_rwlock_write_unlock(ð_globals.protos_lock);
|
---|
[21580dd] | 647 | return EOK;
|
---|
| 648 | }
|
---|
| 649 |
|
---|
[6067284] | 650 | /** Prepares the packet for sending.
|
---|
| 651 | *
|
---|
| 652 | * @param[in] flags The device flags.
|
---|
| 653 | * @param[in] packet The packet.
|
---|
| 654 | * @param[in] src_addr The source hardware address.
|
---|
| 655 | * @param[in] ethertype The ethernet protocol type.
|
---|
| 656 | * @param[in] mtu The device maximum transmission unit.
|
---|
[1bfd3d3] | 657 | * @return EOK on success.
|
---|
| 658 | * @return EINVAL if the packet addresses length is not long
|
---|
[6067284] | 659 | * enough.
|
---|
[1bfd3d3] | 660 | * @return EINVAL if the packet is bigger than the device MTU.
|
---|
| 661 | * @return ENOMEM if there is not enough memory in the packet.
|
---|
[6067284] | 662 | */
|
---|
| 663 | static int
|
---|
[46d4d9f] | 664 | eth_prepare_packet(int flags, packet_t *packet, uint8_t *src_addr, int ethertype,
|
---|
[6067284] | 665 | size_t mtu)
|
---|
| 666 | {
|
---|
[4e5c7ba] | 667 | eth_header_snap_t *header;
|
---|
| 668 | eth_header_lsap_t *header_lsap;
|
---|
| 669 | eth_header_t *header_dix;
|
---|
| 670 | eth_fcs_t *fcs;
|
---|
[6067284] | 671 | uint8_t *src;
|
---|
| 672 | uint8_t *dest;
|
---|
[aadf01e] | 673 | size_t length;
|
---|
| 674 | int i;
|
---|
[6067284] | 675 | void *padding;
|
---|
[4e5c7ba] | 676 | eth_preamble_t *preamble;
|
---|
[aadf01e] | 677 |
|
---|
| 678 | i = packet_get_addr(packet, &src, &dest);
|
---|
[6067284] | 679 | if (i < 0)
|
---|
[aadf01e] | 680 | return i;
|
---|
[609243f4] | 681 |
|
---|
[6067284] | 682 | if (i != ETH_ADDR)
|
---|
[aadf01e] | 683 | return EINVAL;
|
---|
[609243f4] | 684 |
|
---|
| 685 | for (i = 0; i < ETH_ADDR; i++) {
|
---|
| 686 | if (src[i]) {
|
---|
| 687 | src_addr = src;
|
---|
| 688 | break;
|
---|
| 689 | }
|
---|
| 690 | }
|
---|
[6067284] | 691 |
|
---|
[aadf01e] | 692 | length = packet_get_data_length(packet);
|
---|
[6067284] | 693 | if (length > mtu)
|
---|
[aadf01e] | 694 | return EINVAL;
|
---|
[6067284] | 695 |
|
---|
| 696 | if (length < ETH_MIN_TAGGED_CONTENT(flags)) {
|
---|
| 697 | padding = packet_suffix(packet,
|
---|
| 698 | ETH_MIN_TAGGED_CONTENT(flags) - length);
|
---|
| 699 | if (!padding)
|
---|
[aadf01e] | 700 | return ENOMEM;
|
---|
[fb04cba8] | 701 |
|
---|
[aadf01e] | 702 | bzero(padding, ETH_MIN_TAGGED_CONTENT(flags) - length);
|
---|
[21580dd] | 703 | }
|
---|
[6067284] | 704 |
|
---|
| 705 | if (IS_DIX(flags)) {
|
---|
[aadf01e] | 706 | header_dix = PACKET_PREFIX(packet, eth_header_t);
|
---|
[6067284] | 707 | if (!header_dix)
|
---|
[aadf01e] | 708 | return ENOMEM;
|
---|
[6067284] | 709 |
|
---|
[aadf01e] | 710 | header_dix->ethertype = (uint16_t) ethertype;
|
---|
| 711 | memcpy(header_dix->source_address, src_addr, ETH_ADDR);
|
---|
| 712 | memcpy(header_dix->destination_address, dest, ETH_ADDR);
|
---|
| 713 | src = &header_dix->destination_address[0];
|
---|
[609243f4] | 714 | } else if (IS_8023_2_LSAP(flags)) {
|
---|
[aadf01e] | 715 | header_lsap = PACKET_PREFIX(packet, eth_header_lsap_t);
|
---|
[6067284] | 716 | if (!header_lsap)
|
---|
[aadf01e] | 717 | return ENOMEM;
|
---|
[6067284] | 718 |
|
---|
| 719 | header_lsap->header.ethertype = htons(length +
|
---|
| 720 | sizeof(eth_header_lsap_t));
|
---|
[aadf01e] | 721 | header_lsap->lsap.dsap = lsap_unmap(ntohs(ethertype));
|
---|
[21580dd] | 722 | header_lsap->lsap.ssap = header_lsap->lsap.dsap;
|
---|
| 723 | header_lsap->lsap.ctrl = IEEE_8023_2_UI;
|
---|
[aadf01e] | 724 | memcpy(header_lsap->header.source_address, src_addr, ETH_ADDR);
|
---|
| 725 | memcpy(header_lsap->header.destination_address, dest, ETH_ADDR);
|
---|
| 726 | src = &header_lsap->header.destination_address[0];
|
---|
[609243f4] | 727 | } else if (IS_8023_2_SNAP(flags)) {
|
---|
[aadf01e] | 728 | header = PACKET_PREFIX(packet, eth_header_snap_t);
|
---|
[6067284] | 729 | if (!header)
|
---|
[aadf01e] | 730 | return ENOMEM;
|
---|
[6067284] | 731 |
|
---|
| 732 | header->header.ethertype = htons(length +
|
---|
| 733 | sizeof(eth_header_lsap_t) + sizeof(eth_header_snap_t));
|
---|
[aadf01e] | 734 | header->lsap.dsap = (uint16_t) ETH_LSAP_SNAP;
|
---|
[21580dd] | 735 | header->lsap.ssap = header->lsap.dsap;
|
---|
| 736 | header->lsap.ctrl = IEEE_8023_2_UI;
|
---|
[6067284] | 737 |
|
---|
[609243f4] | 738 | for (i = 0; i < 3; i++)
|
---|
[aadf01e] | 739 | header->snap.protocol[i] = 0;
|
---|
[6067284] | 740 |
|
---|
[aadf01e] | 741 | header->snap.ethertype = (uint16_t) ethertype;
|
---|
| 742 | memcpy(header->header.source_address, src_addr, ETH_ADDR);
|
---|
| 743 | memcpy(header->header.destination_address, dest, ETH_ADDR);
|
---|
| 744 | src = &header->header.destination_address[0];
|
---|
[21580dd] | 745 | }
|
---|
[6067284] | 746 |
|
---|
| 747 | if (IS_DUMMY(flags)) {
|
---|
[aadf01e] | 748 | preamble = PACKET_PREFIX(packet, eth_preamble_t);
|
---|
[6067284] | 749 | if (!preamble)
|
---|
[aadf01e] | 750 | return ENOMEM;
|
---|
[6067284] | 751 |
|
---|
[609243f4] | 752 | for (i = 0; i < 7; i++)
|
---|
[aadf01e] | 753 | preamble->preamble[i] = ETH_PREAMBLE;
|
---|
[6067284] | 754 |
|
---|
[80ce111] | 755 | preamble->sfd = ETH_SFD;
|
---|
[6067284] | 756 |
|
---|
[aadf01e] | 757 | fcs = PACKET_SUFFIX(packet, eth_fcs_t);
|
---|
[6067284] | 758 | if (!fcs)
|
---|
[aadf01e] | 759 | return ENOMEM;
|
---|
[6067284] | 760 |
|
---|
| 761 | *fcs = htonl(~compute_crc32(~0U, src, length * 8));
|
---|
[21580dd] | 762 | }
|
---|
[6067284] | 763 |
|
---|
[21580dd] | 764 | return EOK;
|
---|
| 765 | }
|
---|
| 766 |
|
---|
[6067284] | 767 | /** Sends the packet queue.
|
---|
| 768 | *
|
---|
| 769 | * Sends only packet successfully processed by the eth_prepare_packet()
|
---|
| 770 | * function.
|
---|
| 771 | *
|
---|
| 772 | * @param[in] device_id The device identifier.
|
---|
| 773 | * @param[in] packet The packet queue.
|
---|
| 774 | * @param[in] sender The sending module service.
|
---|
[1bfd3d3] | 775 | * @return EOK on success.
|
---|
| 776 | * @return ENOENT if there no such device.
|
---|
| 777 | * @return EINVAL if the service parameter is not known.
|
---|
[6067284] | 778 | */
|
---|
[609243f4] | 779 | static int eth_send_message(nic_device_id_t device_id, packet_t *packet,
|
---|
[fb04cba8] | 780 | services_t sender)
|
---|
[6067284] | 781 | {
|
---|
[4e5c7ba] | 782 | eth_device_t *device;
|
---|
[46d4d9f] | 783 | packet_t *next;
|
---|
| 784 | packet_t *tmp;
|
---|
[aadf01e] | 785 | int ethertype;
|
---|
[4ef32e0c] | 786 | int rc;
|
---|
[21580dd] | 787 |
|
---|
[aadf01e] | 788 | ethertype = htons(protocol_map(SERVICE_ETHERNET, sender));
|
---|
[6067284] | 789 | if (!ethertype) {
|
---|
[6b82009] | 790 | pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
|
---|
[21580dd] | 791 | return EINVAL;
|
---|
| 792 | }
|
---|
[6067284] | 793 |
|
---|
[aadf01e] | 794 | fibril_rwlock_read_lock(ð_globals.devices_lock);
|
---|
| 795 | device = eth_devices_find(ð_globals.devices, device_id);
|
---|
[6067284] | 796 | if (!device) {
|
---|
[aadf01e] | 797 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
[21580dd] | 798 | return ENOENT;
|
---|
| 799 | }
|
---|
[6067284] | 800 |
|
---|
[fb04cba8] | 801 | /* Process packet queue */
|
---|
[21580dd] | 802 | next = packet;
|
---|
[6067284] | 803 | do {
|
---|
[4ef32e0c] | 804 | rc = eth_prepare_packet(device->flags, next,
|
---|
[609243f4] | 805 | (uint8_t *) &device->addr.address, ethertype, device->mtu);
|
---|
[4ef32e0c] | 806 | if (rc != EOK) {
|
---|
[fb04cba8] | 807 | /* Release invalid packet */
|
---|
[aadf01e] | 808 | tmp = pq_detach(next);
|
---|
[6067284] | 809 | if (next == packet)
|
---|
[aadf01e] | 810 | packet = tmp;
|
---|
[6b82009] | 811 | pq_release_remote(eth_globals.net_sess,
|
---|
[6067284] | 812 | packet_get_id(next));
|
---|
[21580dd] | 813 | next = tmp;
|
---|
[6067284] | 814 | } else {
|
---|
[6d8455d] | 815 | nic_send_frame(device->sess, packet_get_data(next),
|
---|
| 816 | packet_get_data_length(next));
|
---|
[aadf01e] | 817 | next = pq_next(next);
|
---|
[21580dd] | 818 | }
|
---|
[609243f4] | 819 | } while (next);
|
---|
[6067284] | 820 |
|
---|
[6d8455d] | 821 | pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
|
---|
[609243f4] | 822 |
|
---|
[aadf01e] | 823 | fibril_rwlock_read_unlock(ð_globals.devices_lock);
|
---|
[21580dd] | 824 | return EOK;
|
---|
| 825 | }
|
---|
| 826 |
|
---|
[1bc35b5] | 827 | static int eth_received(nic_device_id_t device_id)
|
---|
| 828 | {
|
---|
| 829 | void *data;
|
---|
| 830 | size_t size;
|
---|
| 831 | int rc;
|
---|
| 832 |
|
---|
| 833 | rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
|
---|
[8d7ec69d] | 834 | if (rc != EOK) {
|
---|
| 835 | printf("%s: data_write_accept() failed\n", NAME);
|
---|
[1bc35b5] | 836 | return rc;
|
---|
[8d7ec69d] | 837 | }
|
---|
[1bc35b5] | 838 |
|
---|
| 839 | packet_t *packet = packet_get_1_remote(eth_globals.net_sess, size);
|
---|
| 840 | if (packet == NULL)
|
---|
| 841 | return ENOMEM;
|
---|
| 842 |
|
---|
| 843 | void *pdata = packet_suffix(packet, size);
|
---|
| 844 | memcpy(pdata, data, size);
|
---|
| 845 | free(data);
|
---|
| 846 |
|
---|
| 847 | return nil_received_msg_local(device_id, packet);
|
---|
| 848 | }
|
---|
| 849 |
|
---|
[609243f4] | 850 | static int eth_addr_changed(nic_device_id_t device_id)
|
---|
| 851 | {
|
---|
| 852 | nic_address_t address;
|
---|
| 853 | size_t length;
|
---|
| 854 | ipc_callid_t data_callid;
|
---|
| 855 | if (!async_data_write_receive(&data_callid, &length)) {
|
---|
| 856 | async_answer_0(data_callid, EINVAL);
|
---|
| 857 | return EINVAL;
|
---|
| 858 | }
|
---|
| 859 | if (length > sizeof (nic_address_t)) {
|
---|
| 860 | async_answer_0(data_callid, ELIMIT);
|
---|
| 861 | return ELIMIT;
|
---|
| 862 | }
|
---|
| 863 | if (async_data_write_finalize(data_callid, &address, length) != EOK) {
|
---|
| 864 | return EINVAL;
|
---|
| 865 | }
|
---|
| 866 |
|
---|
| 867 | fibril_rwlock_write_lock(ð_globals.devices_lock);
|
---|
| 868 | /* An existing device? */
|
---|
| 869 | eth_device_t *device = eth_devices_find(ð_globals.devices, device_id);
|
---|
| 870 | if (device) {
|
---|
| 871 | printf("Device %d changing address from " PRIMAC " to " PRIMAC "\n",
|
---|
| 872 | device_id, ARGSMAC(device->addr.address), ARGSMAC(address.address));
|
---|
| 873 | memcpy(&device->addr, &address, sizeof (nic_address_t));
|
---|
| 874 | fibril_rwlock_write_unlock(ð_globals.devices_lock);
|
---|
| 875 |
|
---|
| 876 | /* Notify all upper layer modules */
|
---|
| 877 | fibril_rwlock_read_lock(ð_globals.protos_lock);
|
---|
| 878 | int index;
|
---|
| 879 | for (index = 0; index < eth_protos_count(ð_globals.protos); index++) {
|
---|
| 880 | eth_proto_t *proto = eth_protos_get_index(ð_globals.protos, index);
|
---|
| 881 | if (proto->sess != NULL) {
|
---|
| 882 | il_addr_changed_msg(proto->sess, device->device_id,
|
---|
| 883 | ETH_ADDR, address.address);
|
---|
| 884 | }
|
---|
| 885 | }
|
---|
| 886 |
|
---|
| 887 | fibril_rwlock_read_unlock(ð_globals.protos_lock);
|
---|
| 888 | return EOK;
|
---|
| 889 | } else {
|
---|
| 890 | return ENOENT;
|
---|
| 891 | }
|
---|
| 892 | }
|
---|
| 893 |
|
---|
[fe8dfa6] | 894 | int nil_module_message(ipc_callid_t callid, ipc_call_t *call,
|
---|
| 895 | ipc_call_t *answer, size_t *answer_count)
|
---|
[24ab58b3] | 896 | {
|
---|
[46d4d9f] | 897 | packet_t *packet;
|
---|
[e417b96] | 898 | size_t addrlen;
|
---|
| 899 | size_t prefix;
|
---|
| 900 | size_t suffix;
|
---|
| 901 | size_t content;
|
---|
[4ef32e0c] | 902 | int rc;
|
---|
[24ab58b3] | 903 |
|
---|
[aadf01e] | 904 | *answer_count = 0;
|
---|
[79ae36dd] | 905 |
|
---|
| 906 | if (!IPC_GET_IMETHOD(*call))
|
---|
[6067284] | 907 | return EOK;
|
---|
| 908 |
|
---|
[6b82009] | 909 | async_sess_t *callback =
|
---|
| 910 | async_callback_receive_start(EXCHANGE_SERIALIZE, call);
|
---|
| 911 | if (callback)
|
---|
| 912 | return eth_register_message(NIL_GET_PROTO(*call), callback);
|
---|
| 913 |
|
---|
[79ae36dd] | 914 | switch (IPC_GET_IMETHOD(*call)) {
|
---|
[6067284] | 915 | case NET_NIL_DEVICE:
|
---|
[774e6d1a] | 916 | return eth_device_message(IPC_GET_DEVICE(*call),
|
---|
[609243f4] | 917 | IPC_GET_DEVICE_HANDLE(*call), IPC_GET_MTU(*call));
|
---|
[6067284] | 918 | case NET_NIL_SEND:
|
---|
[6b82009] | 919 | rc = packet_translate_remote(eth_globals.net_sess, &packet,
|
---|
[774e6d1a] | 920 | IPC_GET_PACKET(*call));
|
---|
[4ef32e0c] | 921 | if (rc != EOK)
|
---|
| 922 | return rc;
|
---|
[609243f4] | 923 |
|
---|
[774e6d1a] | 924 | return eth_send_message(IPC_GET_DEVICE(*call), packet,
|
---|
| 925 | IPC_GET_SERVICE(*call));
|
---|
[6067284] | 926 | case NET_NIL_PACKET_SPACE:
|
---|
[774e6d1a] | 927 | rc = eth_packet_space_message(IPC_GET_DEVICE(*call), &addrlen,
|
---|
[4ef32e0c] | 928 | &prefix, &content, &suffix);
|
---|
| 929 | if (rc != EOK)
|
---|
| 930 | return rc;
|
---|
[609243f4] | 931 |
|
---|
[774e6d1a] | 932 | IPC_SET_ADDR(*answer, addrlen);
|
---|
| 933 | IPC_SET_PREFIX(*answer, prefix);
|
---|
| 934 | IPC_SET_CONTENT(*answer, content);
|
---|
| 935 | IPC_SET_SUFFIX(*answer, suffix);
|
---|
[6067284] | 936 | *answer_count = 4;
|
---|
[609243f4] | 937 |
|
---|
[6067284] | 938 | return EOK;
|
---|
| 939 | case NET_NIL_ADDR:
|
---|
[609243f4] | 940 | rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_LOCAL_ADDR);
|
---|
[4ef32e0c] | 941 | if (rc != EOK)
|
---|
| 942 | return rc;
|
---|
[609243f4] | 943 |
|
---|
| 944 | IPC_SET_ADDR(*answer, ETH_ADDR);
|
---|
| 945 | *answer_count = 1;
|
---|
| 946 |
|
---|
| 947 | return EOK;
|
---|
[6067284] | 948 | case NET_NIL_BROADCAST_ADDR:
|
---|
[609243f4] | 949 | rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_BROADCAST_ADDR);
|
---|
[4ef32e0c] | 950 | if (rc != EOK)
|
---|
[609243f4] | 951 | return rc;
|
---|
| 952 |
|
---|
| 953 | IPC_SET_ADDR(*answer, ETH_ADDR);
|
---|
| 954 | *answer_count = 1;
|
---|
| 955 |
|
---|
| 956 | return EOK;
|
---|
[21580dd] | 957 | }
|
---|
[24ab58b3] | 958 |
|
---|
[21580dd] | 959 | return ENOTSUP;
|
---|
| 960 | }
|
---|
| 961 |
|
---|
[8d7ec69d] | 962 | static void eth_nic_cb_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
|
---|
| 963 | {
|
---|
| 964 | int rc;
|
---|
| 965 |
|
---|
| 966 | async_answer_0(iid, EOK);
|
---|
| 967 |
|
---|
| 968 | while (true) {
|
---|
| 969 | ipc_call_t call;
|
---|
| 970 | ipc_callid_t callid = async_get_call(&call);
|
---|
| 971 |
|
---|
| 972 | if (!IPC_GET_IMETHOD(call))
|
---|
| 973 | break;
|
---|
| 974 |
|
---|
| 975 | switch (IPC_GET_IMETHOD(call)) {
|
---|
| 976 | case NIC_EV_DEVICE_STATE:
|
---|
| 977 | rc = eth_device_state(IPC_GET_ARG1(call),
|
---|
| 978 | IPC_GET_ARG2(call));
|
---|
| 979 | async_answer_0(callid, (sysarg_t) rc);
|
---|
| 980 | break;
|
---|
| 981 | case NIC_EV_RECEIVED:
|
---|
| 982 | rc = eth_received(IPC_GET_ARG1(call));
|
---|
| 983 | async_answer_0(callid, (sysarg_t) rc);
|
---|
| 984 | break;
|
---|
| 985 | case NIC_EV_ADDR_CHANGED:
|
---|
| 986 | rc = eth_addr_changed(IPC_GET_ARG1(call));
|
---|
| 987 | async_answer_0(callid, (sysarg_t) rc);
|
---|
| 988 | break;
|
---|
| 989 | default:
|
---|
| 990 | async_answer_0(callid, ENOTSUP);
|
---|
| 991 | }
|
---|
| 992 | }
|
---|
| 993 | }
|
---|
| 994 |
|
---|
[849ed54] | 995 | int main(int argc, char *argv[])
|
---|
| 996 | {
|
---|
| 997 | /* Start the module */
|
---|
[fe8dfa6] | 998 | return nil_module_start(SERVICE_ETHERNET);
|
---|
[849ed54] | 999 | }
|
---|
| 1000 |
|
---|
[21580dd] | 1001 | /** @}
|
---|
| 1002 | */
|
---|