--- Gnu-Mach/linux/dev/glue/net.c 2020/09/02 04:41:39 1.1 +++ Gnu-Mach/linux/dev/glue/net.c 2020/09/02 04:54:52 1.1.1.3 @@ -61,6 +61,7 @@ #include #include +#include #include #include @@ -69,6 +70,9 @@ #include #include +#include +#include + #include #include @@ -82,9 +86,9 @@ #include #include #include -#include "device_reply.h" - -#include +#include +#include +#include #define MACH_INCLUDE #include @@ -96,8 +100,9 @@ #include #include #include +#include -extern int linux_intr_pri; +#include /* One of these is associated with each instance of a device. */ struct net_data @@ -248,7 +253,6 @@ void dev_kfree_skb (struct sk_buff *skb, int mode) { unsigned flags; - extern void *io_done_list; /* Queue sk_buff on done list if there is a page list attached or we need to send a reply. @@ -290,6 +294,9 @@ netif_rx (struct sk_buff *skb) eh = (struct ether_header *) (net_kmsg (kmsg)->header); ph = (struct packet_header *) (net_kmsg (kmsg)->packet); memcpy (eh, skb->data, sizeof (struct ether_header)); + + /* packet is prefixed with a struct packet_header, + see include/device/net_status.h. */ memcpy (ph + 1, skb->data + sizeof (struct ether_header), skb->len - sizeof (struct ether_header)); ph->type = eh->ether_type; @@ -298,6 +305,8 @@ netif_rx (struct sk_buff *skb) dev_kfree_skb (skb, FREE_READ); + net_kmsg(kmsg)->sent = FALSE; /* Mark packet as received. */ + /* Pass packet up to the microkernel. */ net_packet (&dev->net_data->ifnet, kmsg, ph->length, ethernet_priority (kmsg)); @@ -393,8 +402,14 @@ device_open (ipc_port_t reply_port, mach } else { - dev->flags |= LINUX_IFF_UP | LINUX_IFF_RUNNING; + /* IPv6 heavily relies on multicasting (especially router and + neighbor solicits and advertisements), so enable reception of + those multicast packets by setting `LINUX_IFF_ALLMULTI'. */ + dev->flags |= LINUX_IFF_UP | LINUX_IFF_RUNNING | LINUX_IFF_ALLMULTI; skb_queue_head_init (&dev->buffs[0]); + + if (dev->set_multicast_list) + dev->set_multicast_list (dev); } if (IP_VALID (reply_port)) ds_device_open_reply (reply_port, reply_port_type, @@ -413,63 +428,43 @@ device_write (void *d, ipc_port_t reply_ int *bytes_written) { unsigned char *p; - int i, amt, skblen, s; - io_return_t err = 0; + int i, s; vm_map_copy_t copy = (vm_map_copy_t) data; + char *map_data; + vm_offset_t map_addr; + vm_size_t map_size; struct net_data *nd = d; struct linux_device *dev = nd->dev; struct sk_buff *skb; + kern_return_t kr; if (count == 0 || count > dev->mtu + dev->hard_header_len) return D_INVALID_SIZE; /* Allocate a sk_buff. */ - amt = PAGE_SIZE - (copy->offset & PAGE_MASK); - skblen = (amt >= count) ? 0 : count; - skb = dev_alloc_skb (skblen); + skb = dev_alloc_skb (count); if (!skb) return D_NO_MEMORY; - /* Copy user data. This is only required if it spans multiple pages. */ - if (skblen == 0) - { - assert (copy->cpy_npages == 1); - - skb->copy = copy; - skb->data = ((void *) copy->cpy_page_list[0]->phys_addr - + (copy->offset & PAGE_MASK)); - skb->len = count; - skb->head = skb->data; - skb->tail = skb->data + skb->len; - skb->end = skb->tail; - } - else - { - skb->len = skblen; - skb->tail = skb->data + skblen; - skb->end = skb->tail; - - memcpy (skb->data, - ((void *) copy->cpy_page_list[0]->phys_addr - + (copy->offset & PAGE_MASK)), - amt); - count -= amt; - p = skb->data + amt; - for (i = 1; count > 0 && i < copy->cpy_npages; i++) - { - amt = PAGE_SIZE; - if (amt > count) - amt = count; - memcpy (p, (void *) copy->cpy_page_list[i]->phys_addr, amt); - count -= amt; - p += amt; - } + /* Map user data. */ + kr = kmem_io_map_copyout(device_io_map, (vm_offset_t *)&map_data, + &map_addr, &map_size, copy, count); - assert (count == 0); + if (kr) { + dev_kfree_skb (skb, FREE_WRITE); + return D_NO_MEMORY; + } - vm_map_copy_discard (copy); - } + /* XXX The underlying physical pages of the mapping could be highmem, + for which drivers require the use of a bounce buffer. */ + memcpy (skb->data, map_data, count); + kmem_io_map_deallocate (device_io_map, map_addr, map_size); + vm_map_copy_discard (copy); + skb->len = count; + skb->head = skb->data; + skb->tail = skb->data + skb->len; + skb->end = skb->tail; skb->dev = dev; skb->reply = reply_port; skb->reply_type = reply_port_type; @@ -484,16 +479,163 @@ device_write (void *d, ipc_port_t reply_ } splx (s); + /* Send packet to filters. */ + { + struct packet_header *packet; + struct ether_header *header; + ipc_kmsg_t kmsg; + + kmsg = net_kmsg_get (); + + if (kmsg != IKM_NULL) + { + /* Suitable for Ethernet only. */ + header = (struct ether_header *) (net_kmsg (kmsg)->header); + packet = (struct packet_header *) (net_kmsg (kmsg)->packet); + memcpy (header, skb->data, sizeof (struct ether_header)); + + /* packet is prefixed with a struct packet_header, + see include/device/net_status.h. */ + memcpy (packet + 1, skb->data + sizeof (struct ether_header), + skb->len - sizeof (struct ether_header)); + packet->length = skb->len - sizeof (struct ether_header) + + sizeof (struct packet_header); + packet->type = header->ether_type; + net_kmsg (kmsg)->sent = TRUE; /* Mark packet as sent. */ + s = splimp (); + net_packet (&dev->net_data->ifnet, kmsg, packet->length, + ethernet_priority (kmsg)); + splx (s); + } + } + return MIG_NO_REPLY; } + static io_return_t device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, mach_msg_type_number_t *count) { - return net_getstat (&((struct net_data *) d)->ifnet, flavor, status, count); + if (flavor == NET_FLAGS) + { + struct net_data *net = (struct net_data *) d; + + if (*count != 1) + return D_INVALID_SIZE; + + status[0] = net->dev->flags; + return D_SUCCESS; + } + + if(flavor >= SIOCIWFIRST && flavor <= SIOCIWLAST) + { + /* handle wireless ioctl */ + if(! IW_IS_GET(flavor)) + return D_INVALID_OPERATION; + + if(*count * sizeof(int) < sizeof(struct ifreq)) + return D_INVALID_OPERATION; + + struct net_data *nd = d; + struct linux_device *dev = nd->dev; + + if(! dev->do_ioctl) + return D_INVALID_OPERATION; + + int result; + + if (flavor == SIOCGIWRANGE || flavor == SIOCGIWENCODE + || flavor == SIOCGIWESSID || flavor == SIOCGIWNICKN + || flavor == SIOCGIWSPY) + { + /* + * These ioctls require an `iw_point' as their argument (i.e. + * they want to return some data to userspace. + * Therefore supply some sane values and carry the data back + * to userspace right behind the `struct iwreq'. + */ + struct iw_point *iwp = &((struct iwreq *) status)->u.data; + iwp->length = *count * sizeof (dev_status_t) - sizeof (struct ifreq); + iwp->pointer = (void *) status + sizeof (struct ifreq); + + result = dev->do_ioctl (dev, (struct ifreq *) status, flavor); + + *count = ((sizeof (struct ifreq) + iwp->length) + / sizeof (dev_status_t)); + if (iwp->length % sizeof (dev_status_t)) + (*count) ++; + } + else + { + *count = sizeof(struct ifreq) / sizeof(int); + result = dev->do_ioctl(dev, (struct ifreq *) status, flavor); + } + + return result ? D_IO_ERROR : D_SUCCESS; + } + else + { + /* common get_status request */ + return net_getstat (&((struct net_data *) d)->ifnet, flavor, + status, count); + } +} + + +static io_return_t +device_set_status(void *d, dev_flavor_t flavor, dev_status_t status, + mach_msg_type_number_t count) +{ + if (flavor == NET_FLAGS) + { + if (count != 1) + return D_INVALID_SIZE; + + short flags = status[0]; + struct net_data *net = (struct net_data *) d; + + dev_change_flags (net->dev, flags); + + /* Change the flags of the Mach device, too. */ + net->ifnet.if_flags = net->dev->flags; + return D_SUCCESS; + } + + if(flavor < SIOCIWFIRST || flavor > SIOCIWLAST) + return D_INVALID_OPERATION; + + if(! IW_IS_SET(flavor)) + return D_INVALID_OPERATION; + + if(count * sizeof(int) < sizeof(struct ifreq)) + return D_INVALID_OPERATION; + + struct net_data *nd = d; + struct linux_device *dev = nd->dev; + + if(! dev->do_ioctl) + return D_INVALID_OPERATION; + + if((flavor == SIOCSIWENCODE || flavor == SIOCSIWESSID + || flavor == SIOCSIWNICKN || flavor == SIOCSIWSPY) + && ((struct iwreq *) status)->u.data.pointer) + { + struct iw_point *iwp = &((struct iwreq *) status)->u.data; + + /* safety check whether the status array is long enough ... */ + if(count * sizeof(int) < sizeof(struct ifreq) + iwp->length) + return D_INVALID_OPERATION; + + /* make sure, iwp->pointer points to the correct address */ + if(iwp->pointer) iwp->pointer = (void *) status + sizeof(struct ifreq); + } + + int result = dev->do_ioctl(dev, (struct ifreq *) status, flavor); + return result ? D_IO_ERROR : D_SUCCESS; } + static io_return_t device_set_filter (void *d, ipc_port_t port, int priority, filter_t * filter, unsigned filter_count) @@ -513,7 +655,7 @@ struct device_emulation_ops linux_net_em NULL, NULL, NULL, - NULL, + device_set_status, device_get_status, device_set_filter, NULL,