/** * Encapsulate and encrypt a packet using CCMP * * @v crypto CCMP cryptosystem * @v iob I/O buffer containing cleartext packet * @ret eiob I/O buffer containing encrypted packet */ struct io_buffer * ccmp_encrypt ( struct net80211_crypto *crypto, struct io_buffer *iob ) { struct ccmp_ctx *ctx = crypto->priv; struct ieee80211_frame *hdr = iob->data; struct io_buffer *eiob; const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; int datalen = iob_len ( iob ) - hdrlen; struct ccmp_head head; struct ccmp_nonce nonce; struct ccmp_aad aad; u8 mic[8], tx_pn[6]; void *edata, *emic; ctx->tx_seq++; u64_to_pn ( ctx->tx_seq, tx_pn, PN_LSB ); /* Allocate memory */ eiob = alloc_iob ( iob_len ( iob ) + CCMP_HEAD_LEN + CCMP_MIC_LEN ); if ( ! eiob ) return NULL; /* Copy frame header */ memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen ); hdr = eiob->data; hdr->fc |= IEEE80211_FC_PROTECTED; /* Fill in packet number and extended IV */ memcpy ( head.pn_lo, tx_pn, 2 ); memcpy ( head.pn_hi, tx_pn + 2, 4 ); head.kid = 0x20; /* have Extended IV, key ID 0 */ head._rsvd = 0; memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) ); /* Form nonce */ nonce.prio = 0; memcpy ( nonce.a2, hdr->addr2, ETH_ALEN ); u64_to_pn ( ctx->tx_seq, nonce.pn, PN_MSB ); /* Form additional authentication data */ aad.fc = hdr->fc & CCMP_AAD_FC_MASK; memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */ aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK; /* Calculate MIC over the data */ ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, mic ); /* Copy and encrypt data and MIC */ edata = iob_put ( eiob, datalen ); emic = iob_put ( eiob, CCMP_MIC_LEN ); ccmp_ctr_xor ( ctx, &nonce, iob->data + hdrlen, edata, datalen, mic, emic ); /* Done! */ DBGC2 ( ctx, "WPA-CCMP %p: encrypted packet %p -> %p\n", ctx, iob, eiob ); return eiob; }
/** * Transmit SRP SCSI command * * @v srp SRP device */ static void srp_cmd ( struct srp_device *srp ) { struct io_buffer *iobuf; struct srp_cmd *cmd; struct srp_memory_descriptor *data_out; struct srp_memory_descriptor *data_in; int rc; assert ( srp->state & SRP_STATE_LOGGED_IN ); /* Allocate I/O buffer */ iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN ); if ( ! iobuf ) { rc = -ENOMEM; goto err; } /* Construct base portion */ cmd = iob_put ( iobuf, sizeof ( *cmd ) ); memset ( cmd, 0, sizeof ( *cmd ) ); cmd->type = SRP_CMD; cmd->tag.dwords[1] = htonl ( ++srp_tag ); cmd->lun = srp->lun; memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) ); /* Construct data-out descriptor, if present */ if ( srp->command->data_out ) { cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT; data_out = iob_put ( iobuf, sizeof ( *data_out ) ); data_out->address = cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) ); data_out->handle = ntohl ( srp->memory_handle ); data_out->len = ntohl ( srp->command->data_out_len ); } /* Construct data-in descriptor, if present */ if ( srp->command->data_in ) { cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT; data_in = iob_put ( iobuf, sizeof ( *data_in ) ); data_in->address = cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) ); data_in->handle = ntohl ( srp->memory_handle ); data_in->len = ntohl ( srp->command->data_in_len ); } DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp, ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) ); DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); /* Send IU */ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { DBGC ( srp, "SRP %p could not send command: %s\n", srp, strerror ( rc ) ); goto err; } return; err: srp_fail ( srp, rc ); }
/** * Look up media-specific link-layer address in the ARP cache * * @v netdev Network device * @v net_protocol Network-layer protocol * @v dest_net_addr Destination network-layer address * @v source_net_addr Source network-layer address * @ret dest_ll_addr Destination link layer address * @ret rc Return status code * * This function will use the ARP cache to look up the link-layer * address for the link-layer protocol associated with the network * device and the given network-layer protocol and addresses. If * found, the destination link-layer address will be filled in in @c * dest_ll_addr. * * If no address is found in the ARP cache, an ARP request will be * transmitted on the specified network device and -ENOENT will be * returned. */ int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol, const void *dest_net_addr, const void *source_net_addr, void *dest_ll_addr ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; const struct arp_entry *arp; struct io_buffer *iobuf; struct arphdr *arphdr; int rc; /* Look for existing entry in ARP table */ arp = arp_find_entry ( ll_protocol, net_protocol, dest_net_addr ); if ( arp ) { DBG ( "ARP cache hit: %s %s => %s %s\n", net_protocol->name, net_protocol->ntoa ( arp->net_addr ), ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) ); memcpy ( dest_ll_addr, arp->ll_addr, ll_protocol->ll_addr_len); return 0; } DBG ( "ARP cache miss: %s %s\n", net_protocol->name, net_protocol->ntoa ( dest_net_addr ) ); /* Allocate ARP packet */ iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) + 2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) ); if ( ! iobuf ) return -ENOMEM; iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); /* Build up ARP request */ arphdr = iob_put ( iobuf, sizeof ( *arphdr ) ); arphdr->ar_hrd = ll_protocol->ll_proto; arphdr->ar_hln = ll_protocol->ll_addr_len; arphdr->ar_pro = net_protocol->net_proto; arphdr->ar_pln = net_protocol->net_addr_len; arphdr->ar_op = htons ( ARPOP_REQUEST ); memcpy ( iob_put ( iobuf, ll_protocol->ll_addr_len ), netdev->ll_addr, ll_protocol->ll_addr_len ); memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), source_net_addr, net_protocol->net_addr_len ); memset ( iob_put ( iobuf, ll_protocol->ll_addr_len ), 0, ll_protocol->ll_addr_len ); memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), dest_net_addr, net_protocol->net_addr_len ); /* Transmit ARP request */ if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol, netdev->ll_broadcast, netdev->ll_addr ) ) != 0 ) return rc; return -ENOENT; }
/** * Concatenate I/O buffers into a single buffer * * @v list List of I/O buffers * @ret iobuf Concatenated I/O buffer, or NULL on allocation failure * * After a successful concatenation, the list will be empty. */ struct io_buffer * iob_concatenate ( struct list_head *list ) { struct io_buffer *iobuf; struct io_buffer *tmp; struct io_buffer *concatenated; size_t len = 0; /* If the list contains only a single entry, avoid an * unnecessary additional allocation. */ if ( list_is_singular ( list ) ) { iobuf = list_first_entry ( list, struct io_buffer, list ); INIT_LIST_HEAD ( list ); return iobuf; } /* Calculate total length */ list_for_each_entry ( iobuf, list, list ) len += iob_len ( iobuf ); /* Allocate new I/O buffer */ concatenated = alloc_iob_raw ( len, __alignof__ ( *iobuf ), 0 ); if ( ! concatenated ) return NULL; /* Move data to new I/O buffer */ list_for_each_entry_safe ( iobuf, tmp, list, list ) { list_del ( &iobuf->list ); memcpy ( iob_put ( concatenated, iob_len ( iobuf ) ), iobuf->data, iob_len ( iobuf ) ); free_iob ( iobuf ); }
/** * Add a file handle to the end of an I/O buffer * * @v io_buf I/O buffer * @v fh File handle * @ret size Size of the data written */ size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ) { size_t s; s = oncrpc_iob_add_int ( io_buf, fh->size ); memcpy ( iob_put ( io_buf, fh->size ), &fh->fh, fh->size ); return s + fh->size; }
/** * Transmit NDP packet with link-layer address option * * @v netdev Network device * @v sin6_src Source socket address * @v sin6_dest Destination socket address * @v data NDP header * @v len Size of NDP header * @v option_type NDP option type * @ret rc Return status code */ static int ndp_tx_ll_addr ( struct net_device *netdev, struct sockaddr_in6 *sin6_src, struct sockaddr_in6 *sin6_dest, const void *data, size_t len, unsigned int option_type ) { struct sockaddr_tcpip *st_src = ( ( struct sockaddr_tcpip * ) sin6_src ); struct sockaddr_tcpip *st_dest = ( ( struct sockaddr_tcpip * ) sin6_dest ); struct ll_protocol *ll_protocol = netdev->ll_protocol; struct io_buffer *iobuf; struct ndp_ll_addr_option *ll_addr_opt; union ndp_header *ndp; size_t option_len; int rc; /* Allocate and populate buffer */ option_len = ( ( sizeof ( *ll_addr_opt ) + ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) & ~( NDP_OPTION_BLKSZ - 1 ) ); iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len + option_len ); if ( ! iobuf ) return -ENOMEM; iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN ); memcpy ( iob_put ( iobuf, len ), data, len ); ll_addr_opt = iob_put ( iobuf, option_len ); ll_addr_opt->header.type = option_type; ll_addr_opt->header.blocks = ( option_len / NDP_OPTION_BLKSZ ); memcpy ( ll_addr_opt->ll_addr, netdev->ll_addr, ll_protocol->ll_addr_len ); ndp = iobuf->data; ndp->icmp.chksum = tcpip_chksum ( ndp, ( len + option_len ) ); /* Transmit packet */ if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest, netdev, &ndp->icmp.chksum ) ) != 0 ) { DBGC ( netdev, "NDP could not transmit packet: %s\n", strerror ( rc ) ); return rc; } return 0; }
/** * e1000_process_rx_packets - process received packets * * @v netdev network interface device structure **/ static void e1000e_process_rx_packets ( struct net_device *netdev ) { struct e1000_adapter *adapter = netdev_priv ( netdev ); uint32_t i; uint32_t rx_status; uint32_t rx_len; uint32_t rx_err; struct e1000_rx_desc *rx_curr_desc; /* Process received packets */ while ( 1 ) { i = adapter->rx_curr; rx_curr_desc = ( void * ) ( adapter->rx_base ) + ( i * sizeof ( *adapter->rx_base ) ); rx_status = rx_curr_desc->status; DBG2 ( "Before DD Check RX_status: %#08x\n", rx_status ); if ( ! ( rx_status & E1000_RXD_STAT_DD ) ) break; if ( adapter->rx_iobuf[i] == NULL ) break; DBG ( "E1000_RCTL = %#08x\n", E1000_READ_REG ( &adapter->hw, E1000_RCTL ) ); rx_len = rx_curr_desc->length; DBG ( "Received packet, rx_curr: %d rx_status: %#08x rx_len: %d\n", i, rx_status, rx_len ); rx_err = rx_curr_desc->errors; iob_put ( adapter->rx_iobuf[i], rx_len ); if ( rx_err & E1000_RXD_ERR_FRAME_ERR_MASK ) { netdev_rx_err ( netdev, adapter->rx_iobuf[i], -EINVAL ); DBG ( "e1000_poll: Corrupted packet received!" " rx_err: %#08x\n", rx_err ); } else { /* Add this packet to the receive queue. */ netdev_rx ( netdev, adapter->rx_iobuf[i] ); } adapter->rx_iobuf[i] = NULL; memset ( rx_curr_desc, 0, sizeof ( *rx_curr_desc ) ); adapter->rx_curr = ( adapter->rx_curr + 1 ) % NUM_RX_DESC; } }
static void rtl818x_handle_rx(struct net80211_device *dev) { struct rtl818x_priv *priv = dev->priv; unsigned int count = RTL818X_RX_RING_SIZE; while (count--) { struct rtl818x_rx_desc *entry = &priv->rx_ring[priv->rx_idx]; struct io_buffer *iob = priv->rx_buf[priv->rx_idx]; u32 flags = le32_to_cpu(entry->flags); if (flags & RTL818X_RX_DESC_FLAG_OWN) return; if (flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL | RTL818X_RX_DESC_FLAG_FOF | RTL818X_RX_DESC_FLAG_RX_ERR)) { /* This is crappy hardware. The Linux driver doesn't even log these. */ goto done; } else if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) { /* This is actually a corrupt packet. */ DBG2("rtl818x RX:%d CRC fail: flags %08x\n", priv->rx_idx, flags); net80211_rx_err(dev, NULL, EIO); } else { u32 flags2 = le32_to_cpu(entry->flags2); struct io_buffer *new_iob = alloc_iob(MAX_RX_SIZE); if (!new_iob) { net80211_rx_err(dev, NULL, ENOMEM); goto done; } DBGP("rtl818x RX:%d success: flags %08x %08x\n", priv->rx_idx, flags, flags2); iob_put(iob, flags & 0xFFF); net80211_rx(dev, iob, (flags2 >> 8) & 0x7f, rtl818x_rates[(flags >> 20) & 0xf]); iob = new_iob; priv->rx_buf[priv->rx_idx] = iob; } done: entry->rx_buf = cpu_to_le32(virt_to_bus(iob->data)); entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE); if (priv->rx_idx == RTL818X_RX_RING_SIZE - 1) entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); priv->rx_idx = (priv->rx_idx + 1) % RTL818X_RX_RING_SIZE; } }
/** * Deliver datagram as raw data * * @v intf Data transfer interface * @v data Data * @v len Length of data * @v meta Data transfer metadata * @ret rc Return status code */ int xfer_deliver_raw_meta ( struct interface *intf, const void *data, size_t len, struct xfer_metadata *meta ) { struct io_buffer *iobuf; iobuf = xfer_alloc_iob ( intf, len ); if ( ! iobuf ) return -ENOMEM; memcpy ( iob_put ( iobuf, len ), data, len ); return xfer_deliver ( intf, iobuf, meta ); }
static void b44_process_rx_packets(struct b44_private *bp) { struct io_buffer *iob; /* received data */ struct rx_header *rh; u32 pending, i; u16 len; pending = pending_rx_index(bp); for (i = bp->rx_cur; i != pending; i = ring_next(i)) { iob = bp->rx_iobuf[i]; if (iob == NULL) break; rh = iob->data; len = le16_to_cpu(rh->len); /* * Guard against incompletely written RX descriptors. * Without this, things can get really slow! */ if (len == 0) break; /* Discard CRC that is generated by the card */ len -= 4; /* Check for invalid packets and errors */ if (len > RX_PKT_BUF_SZ - RX_PKT_OFFSET || (rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) { DBG("rx error len=%d flags=%04x\n", len, cpu_to_le16(rh->flags)); rh->len = 0; rh->flags = 0; netdev_rx_err(bp->netdev, iob, -EINVAL); continue; } /* Clear RX descriptor */ rh->len = 0; rh->flags = 0; bp->rx_iobuf[i] = NULL; /* Hand off the IO buffer to the network stack */ iob_reserve(iob, RX_PKT_OFFSET); iob_put(iob, len); netdev_rx(bp->netdev, iob); } bp->rx_cur = i; b44_rx_refill(bp, pending_rx_index(bp)); }
/** * Transmit NDP neighbour solicitation/advertisement packet * * @v netdev Network device * @v sin6_src Source socket address * @v sin6_dest Destination socket address * @v target Neighbour target address * @v icmp_type ICMPv6 type * @v flags NDP flags * @v option_type NDP option type * @ret rc Return status code */ static int ndp_tx_neighbour ( struct net_device *netdev, struct sockaddr_in6 *sin6_src, struct sockaddr_in6 *sin6_dest, const struct in6_addr *target, unsigned int icmp_type, unsigned int flags, unsigned int option_type ) { struct sockaddr_tcpip *st_src = ( ( struct sockaddr_tcpip * ) sin6_src ); struct sockaddr_tcpip *st_dest = ( ( struct sockaddr_tcpip * ) sin6_dest ); struct ll_protocol *ll_protocol = netdev->ll_protocol; struct io_buffer *iobuf; struct ndp_neighbour_header *neigh; struct ndp_ll_addr_option *ll_addr_opt; size_t option_len; size_t len; int rc; /* Allocate and populate buffer */ option_len = ( ( sizeof ( *ll_addr_opt ) + ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) & ~( NDP_OPTION_BLKSZ - 1 ) ); len = ( sizeof ( *neigh ) + option_len ); iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len ); if ( ! iobuf ) return -ENOMEM; iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN ); neigh = iob_put ( iobuf, len ); memset ( neigh, 0, len ); neigh->icmp.type = icmp_type; neigh->flags = flags; memcpy ( &neigh->target, target, sizeof ( neigh->target ) ); ll_addr_opt = &neigh->option[0].ll_addr; ll_addr_opt->header.type = option_type; ll_addr_opt->header.blocks = ( option_len / NDP_OPTION_BLKSZ ); memcpy ( ll_addr_opt->ll_addr, netdev->ll_addr, ll_protocol->ll_addr_len ); neigh->icmp.chksum = tcpip_chksum ( neigh, len ); /* Transmit packet */ if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest, netdev, &neigh->icmp.chksum ) ) != 0 ) { DBGC ( netdev, "NDP could not transmit packet: %s\n", strerror ( rc ) ); return rc; } return 0; }
/** * Poll for received packets * * @v netdev Network device */ static void nii_poll_rx ( struct net_device *netdev ) { struct nii_nic *nii = netdev->priv; PXE_CPB_RECEIVE cpb; PXE_DB_RECEIVE db; unsigned int quota; int stat; int rc; /* Retrieve up to NII_RX_QUOTA packets */ for ( quota = NII_RX_QUOTA ; quota ; quota-- ) { /* Allocate buffer, if required */ if ( ! nii->rxbuf ) { nii->rxbuf = alloc_iob ( nii->mtu ); if ( ! nii->rxbuf ) { /* Leave for next poll */ break; } } /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); cpb.BufferAddr = virt_to_bus ( nii->rxbuf->data ); cpb.BufferLen = iob_tailroom ( nii->rxbuf ); /* Issue command */ if ( ( stat = nii_issue_cpb_db ( nii, PXE_OPCODE_RECEIVE, &cpb, sizeof ( cpb ), &db, sizeof ( db ) ) ) < 0 ) { /* PXE_STATCODE_NO_DATA is just the usual "no packet" * status indicator; ignore it. */ if ( stat == -PXE_STATCODE_NO_DATA ) break; /* Anything else is an error */ rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not receive: %s\n", nii->dev.name, strerror ( rc ) ); netdev_rx_err ( netdev, NULL, rc ); break; } /* Hand off to network stack */ iob_put ( nii->rxbuf, db.FrameLen ); netdev_rx ( netdev, nii->rxbuf ); nii->rxbuf = NULL; } }
static void gdbudp_send ( const char *buf, size_t len ) { struct io_buffer *iob; struct ethhdr *ethhdr; struct iphdr *iphdr; struct udp_header *udphdr; /* Check that we are connected */ if ( dest_addr.sin_port == 0 ) { return; } gdbudp_ensure_netdev_open ( netdev ); iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len ); if ( !iob ) { return; } /* Payload */ iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) ); memcpy ( iob_put ( iob, len ), buf, len ); /* UDP header */ udphdr = iob_push ( iob, sizeof ( *udphdr ) ); udphdr->src = source_addr.sin_port; udphdr->dest = dest_addr.sin_port; udphdr->len = htons ( iob_len ( iob ) ); udphdr->chksum = 0; /* optional and we are not using it */ /* IP header */ iphdr = iob_push ( iob, sizeof ( *iphdr ) ); memset ( iphdr, 0, sizeof ( *iphdr ) ); iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); iphdr->service = IP_TOS; iphdr->len = htons ( iob_len ( iob ) ); iphdr->ttl = IP_TTL; iphdr->protocol = IP_UDP; iphdr->dest.s_addr = dest_addr.sin_addr.s_addr; iphdr->src.s_addr = source_addr.sin_addr.s_addr; iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); /* Ethernet header */ ethhdr = iob_push ( iob, sizeof ( *ethhdr ) ); memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN ); memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN ); ethhdr->h_protocol = htons ( ETH_P_IP ); netdev_tx ( netdev, iob ); }
/** * Initiate SRP login * * @v srp SRP device */ static void srp_login ( struct srp_device *srp ) { struct io_buffer *iobuf; struct srp_login_req *login_req; int rc; assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ); /* Open underlying socket */ if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) { DBGC ( srp, "SRP %p could not open socket: %s\n", srp, strerror ( rc ) ); goto err; } srp->state |= SRP_STATE_SOCKET_OPEN; /* Allocate I/O buffer */ iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) ); if ( ! iobuf ) { rc = -ENOMEM; goto err; } /* Construct login request IU */ login_req = iob_put ( iobuf, sizeof ( *login_req ) ); memset ( login_req, 0, sizeof ( *login_req ) ); login_req->type = SRP_LOGIN_REQ; login_req->tag.dwords[1] = htonl ( ++srp_tag ); login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN ); login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD; memcpy ( &login_req->port_ids, &srp->port_ids, sizeof ( login_req->port_ids ) ); DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n", srp, ntohl ( login_req->tag.dwords[0] ), ntohl ( login_req->tag.dwords[1] ) ); DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); /* Send login request IU */ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { DBGC ( srp, "SRP %p could not send login request: %s\n", srp, strerror ( rc ) ); goto err; } return; err: srp_fail ( srp, rc ); }
static void legacy_poll ( struct net_device *netdev ) { struct nic *nic = netdev->priv; struct io_buffer *iobuf; iobuf = alloc_iob ( ETH_FRAME_LEN ); if ( ! iobuf ) return; nic->packet = iobuf->data; if ( nic->nic_op->poll ( nic, 1 ) ) { DBG ( "Received %d bytes\n", nic->packetlen ); iob_put ( iobuf, nic->packetlen ); netdev_rx ( netdev, iobuf ); } else { free_iob ( iobuf ); } }
/** * Receive control packet * * @v acm USB RNDIS device * @ret rc Return status code */ static int acm_control_receive ( struct acm_device *acm ) { struct rndis_device *rndis = acm->rndis; struct usb_device *usb = acm->usb; struct io_buffer *iobuf; struct rndis_header *header; size_t mtu = ACM_RESPONSE_MTU; size_t len; int rc; /* Allocate I/O buffer */ iobuf = alloc_iob ( mtu ); if ( ! iobuf ) { rc = -ENOMEM; goto err_alloc; } /* Get encapsulated response */ if ( ( rc = cdc_get_encapsulated_response ( usb, acm->usbnet.comms, iobuf->data, mtu ) ) != 0 ){ DBGC ( acm, "ACM %p could not get encapsulated response: %s\n", acm, strerror ( rc ) ); goto err_get_response; } /* Fix up buffer length */ header = iobuf->data; len = le32_to_cpu ( header->len ); if ( len > mtu ) { DBGC ( acm, "ACM %p overlength encapsulated response\n", acm ); DBGC_HDA ( acm, 0, iobuf->data, mtu ); rc = -EPROTO; goto err_len; } iob_put ( iobuf, len ); /* Hand off to RNDIS */ rndis_rx ( rndis, iob_disown ( iobuf ) ); return 0; err_len: err_get_response: free_iob ( iobuf ); err_alloc: return rc; }
/** * Poll for received packets * * @v netdev Network device */ static void snpnet_poll_rx ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; UINTN len; unsigned int quota; EFI_STATUS efirc; int rc; /* Retrieve up to SNP_RX_QUOTA packets */ for ( quota = SNP_RX_QUOTA ; quota ; quota-- ) { /* Allocate buffer, if required */ if ( ! snp->rxbuf ) { snp->rxbuf = alloc_iob ( snp->mtu ); if ( ! snp->rxbuf ) { /* Leave for next poll */ break; } } /* Receive packet */ len = iob_tailroom ( snp->rxbuf ); if ( ( efirc = snp->snp->Receive ( snp->snp, NULL, &len, snp->rxbuf->data, NULL, NULL, NULL ) ) != 0 ) { /* EFI_NOT_READY is just the usual "no packet" * status indication; ignore it. */ if ( efirc == EFI_NOT_READY ) break; /* Anything else is an error */ rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not receive: %s\n", netdev->name, strerror ( rc ) ); netdev_rx_err ( netdev, NULL, rc ); break; } /* Hand off to network stack */ iob_put ( snp->rxbuf, len ); netdev_rx ( netdev, snp->rxbuf ); snp->rxbuf = NULL; } }
/** Poll for new packets */ static void af_packet_nic_poll ( struct net_device *netdev ) { struct af_packet_nic * nic = netdev->priv; struct pollfd pfd; struct io_buffer * iobuf; int r; pfd.fd = nic->fd; pfd.events = POLLIN; if (linux_poll(&pfd, 1, 0) == -1) { DBGC(nic, "af_packet %p poll failed (%s)\n", nic, linux_strerror(linux_errno)); return; } if ((pfd.revents & POLLIN) == 0) return; /* At this point we know there is at least one new packet to be read */ iobuf = alloc_iob(RX_BUF_SIZE); if (! iobuf) goto allocfail; while ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0) { DBGC2(nic, "af_packet %p read %d bytes\n", nic, r); iob_put(iobuf, r); netdev_rx(netdev, iobuf); iobuf = alloc_iob(RX_BUF_SIZE); if (! iobuf) goto allocfail; } free_iob(iobuf); return; allocfail: DBGC(nic, "af_packet %p alloc_iob failed\n", nic); }
/** * Poll for received packets * * @v netdev Network device */ static void rhine_poll_rx ( struct net_device *netdev ) { struct rhine_nic *rhn = netdev->priv; struct rhine_descriptor *desc; struct io_buffer *iobuf; unsigned int rx_idx; uint32_t des0; size_t len; /* Check for received packets */ while ( rhn->rx.cons != rhn->rx.prod ) { /* Get next receive descriptor */ rx_idx = ( rhn->rx.cons % RHINE_RXDESC_NUM ); desc = &rhn->rx.desc[rx_idx]; /* Stop if descriptor is still in use */ if ( desc->des0 & cpu_to_le32 ( RHINE_DES0_OWN ) ) return; /* Populate I/O buffer */ iobuf = rhn->rx_iobuf[rx_idx]; rhn->rx_iobuf[rx_idx] = NULL; des0 = le32_to_cpu ( desc->des0 ); len = ( RHINE_DES0_GETSIZE ( des0 ) - 4 /* strip CRC */ ); iob_put ( iobuf, len ); /* Hand off to network stack */ if ( des0 & RHINE_RDES0_RXOK ) { DBGC2 ( rhn, "RHINE %p RX %d complete (length %zd)\n", rhn, rx_idx, len ); netdev_rx ( netdev, iobuf ); } else { DBGC ( rhn, "RHINE %p RX %d error (length %zd, DES0 " "%08x)\n", rhn, rx_idx, len, des0 ); netdev_rx_err ( netdev, iobuf, -EIO ); } rhn->rx.cons++; } }
/** * Poll for received packets * * @v netdev Network device */ static void myson_poll_rx ( struct net_device *netdev ) { struct myson_nic *myson = netdev->priv; struct myson_descriptor *rx; struct io_buffer *iobuf; unsigned int rx_idx; size_t len; /* Check for received packets */ while ( myson->rx.cons != myson->rx.prod ) { /* Get next receive descriptor */ rx_idx = ( myson->rx.cons % MYSON_NUM_RX_DESC ); rx = &myson->rx.desc[rx_idx]; /* Stop if descriptor is still in use */ if ( rx->status & MYSON_RX_STAT_OWN ) return; /* Populate I/O buffer */ iobuf = myson->rx_iobuf[rx_idx]; myson->rx_iobuf[rx_idx] = NULL; len = MYSON_RX_STAT_FLNG ( le32_to_cpu ( rx->status ) ); iob_put ( iobuf, len - 4 /* strip CRC */ ); /* Hand off to network stack */ if ( rx->status & cpu_to_le32 ( MYSON_RX_STAT_ES ) ) { DBGC ( myson, "MYSON %p RX %d error (length %zd, " "status %08x)\n", myson, rx_idx, len, le32_to_cpu ( rx->status ) ); netdev_rx_err ( netdev, iobuf, -EIO ); } else { DBGC2 ( myson, "MYSON %p RX %d complete (length " "%zd)\n", myson, rx_idx, len ); netdev_rx ( netdev, iobuf ); } myson->rx.cons++; } }
/** * Poll for received packets * * @v netdev Network device */ static void netfront_poll_rx ( struct net_device *netdev ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; struct netif_rx_response *response; struct io_buffer *iobuf; int status; size_t len; int rc; /* Consume any unconsumed responses */ while ( RING_HAS_UNCONSUMED_RESPONSES ( &netfront->rx_fring ) ) { /* Get next response */ response = RING_GET_RESPONSE ( &netfront->rx_fring, netfront->rx_fring.rsp_cons++ ); /* Retrieve from descriptor ring */ iobuf = netfront_pull ( netfront, &netfront->rx, response->id ); status = response->status; if ( status >= 0 ) { len = status; iob_reserve ( iobuf, response->offset ); iob_put ( iobuf, len ); DBGC2 ( netfront, "NETFRONT %s RX id %d complete " "%#08lx+%zx\n", xendev->key, response->id, virt_to_phys ( iobuf->data ), len ); netdev_rx ( netdev, iobuf ); } else { rc = -EIO_NETIF_RSP ( status ); DBGC2 ( netfront, "NETFRONT %s RX id %d error %d: %s\n", xendev->key, response->id, status, strerror ( rc ) ); netdev_rx_err ( netdev, iobuf, rc ); } } }
/* PXENV_UNDI_TRANSMIT * * Status: working */ static PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT *undi_transmit ) { struct s_PXENV_UNDI_TBD tbd; struct DataBlk *datablk; struct io_buffer *iobuf; struct net_protocol *net_protocol; struct ll_protocol *ll_protocol; char destaddr[MAX_LL_ADDR_LEN]; const void *ll_dest; size_t len; unsigned int i; int rc; /* Start profiling */ profile_start ( &undi_tx_profiler ); /* Sanity check */ if ( ! pxe_netdev ) { DBGC ( &pxe_netdev, "PXENV_UNDI_TRANSMIT called with no " "network device\n" ); undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_STATE; return PXENV_EXIT_FAILURE; } DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT" ); /* Forcibly enable interrupts and freeze receive queue * processing at this point, to work around callers that never * call PXENV_UNDI_OPEN before attempting to use the UNDI API. */ if ( ! netdev_rx_frozen ( pxe_netdev ) ) { netdev_rx_freeze ( pxe_netdev ); netdev_irq ( pxe_netdev, 1 ); } /* Identify network-layer protocol */ switch ( undi_transmit->Protocol ) { case P_IP: net_protocol = &ipv4_protocol; break; case P_ARP: net_protocol = &arp_protocol; break; case P_RARP: net_protocol = &rarp_protocol; break; case P_UNKNOWN: net_protocol = NULL; break; default: DBGC2 ( &pxe_netdev, " %02x invalid protocol\n", undi_transmit->Protocol ); undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; return PXENV_EXIT_FAILURE; } DBGC2 ( &pxe_netdev, " %s", ( net_protocol ? net_protocol->name : "RAW" ) ); /* Calculate total packet length */ copy_from_real ( &tbd, undi_transmit->TBD.segment, undi_transmit->TBD.offset, sizeof ( tbd ) ); len = tbd.ImmedLength; DBGC2 ( &pxe_netdev, " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset, tbd.ImmedLength ); for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { datablk = &tbd.DataBlock[i]; len += datablk->TDDataLen; DBGC2 ( &pxe_netdev, " %04x:%04x+%x", datablk->TDDataPtr.segment, datablk->TDDataPtr.offset, datablk->TDDataLen ); } /* Allocate and fill I/O buffer */ iobuf = alloc_iob ( MAX_LL_HEADER_LEN + ( ( len > IOB_ZLEN ) ? len : IOB_ZLEN ) ); if ( ! iobuf ) { DBGC2 ( &pxe_netdev, " could not allocate iobuf\n" ); undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES; return PXENV_EXIT_FAILURE; } iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment, tbd.Xmit.offset, tbd.ImmedLength ); for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { datablk = &tbd.DataBlock[i]; copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ), datablk->TDDataPtr.segment, datablk->TDDataPtr.offset, datablk->TDDataLen ); } /* Add link-layer header, if required to do so */ if ( net_protocol != NULL ) { /* Calculate destination address */ ll_protocol = pxe_netdev->ll_protocol; if ( undi_transmit->XmitFlag == XMT_DESTADDR ) { copy_from_real ( destaddr, undi_transmit->DestAddr.segment, undi_transmit->DestAddr.offset, ll_protocol->ll_addr_len ); ll_dest = destaddr; DBGC2 ( &pxe_netdev, " DEST %s", ll_protocol->ntoa ( ll_dest ) ); } else { ll_dest = pxe_netdev->ll_broadcast; DBGC2 ( &pxe_netdev, " BCAST" ); } /* Add link-layer header */ if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest, pxe_netdev->ll_addr, net_protocol->net_proto ))!=0){ DBGC2 ( &pxe_netdev, " could not add link-layer " "header: %s\n", strerror ( rc ) ); free_iob ( iobuf ); undi_transmit->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } } /* Flag transmission as in-progress. Do this before starting * to transmit the packet, because the ISR may trigger before * we return from netdev_tx(). */ undi_tx_count++; /* Transmit packet */ DBGC2 ( &pxe_netdev, "\n" ); if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) { DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT could not transmit: " "%s\n", strerror ( rc ) ); undi_tx_count--; undi_transmit->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } profile_stop ( &undi_tx_profiler ); undi_transmit->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; }
/** * e1000_poll - Poll for received packets * * @v netdev Network device */ static void e1000_poll ( struct net_device *netdev ) { struct e1000_adapter *adapter = netdev_priv( netdev ); struct e1000_hw *hw = &adapter->hw; uint32_t icr; uint32_t tx_status; uint32_t rx_status; uint32_t rx_len; uint32_t rx_err; struct e1000_tx_desc *tx_curr_desc; struct e1000_rx_desc *rx_curr_desc; uint32_t i; DBGP ( "e1000_poll\n" ); /* Acknowledge interrupts */ icr = E1000_READ_REG ( hw, ICR ); if ( ! icr ) return; DBG ( "e1000_poll: intr_status = %#08x\n", icr ); /* Check status of transmitted packets */ while ( ( i = adapter->tx_head ) != adapter->tx_tail ) { tx_curr_desc = ( void * ) ( adapter->tx_base ) + ( i * sizeof ( *adapter->tx_base ) ); tx_status = tx_curr_desc->upper.data; /* if the packet at tx_head is not owned by hardware it is for us */ if ( ! ( tx_status & E1000_TXD_STAT_DD ) ) break; DBG ( "Sent packet. tx_head: %d tx_tail: %d tx_status: %#08x\n", adapter->tx_head, adapter->tx_tail, tx_status ); if ( tx_status & ( E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU ) ) { netdev_tx_complete_err ( netdev, adapter->tx_iobuf[i], -EINVAL ); DBG ( "Error transmitting packet, tx_status: %#08x\n", tx_status ); } else { netdev_tx_complete ( netdev, adapter->tx_iobuf[i] ); DBG ( "Success transmitting packet, tx_status: %#08x\n", tx_status ); } /* Decrement count of used descriptors, clear this descriptor */ adapter->tx_fill_ctr--; memset ( tx_curr_desc, 0, sizeof ( *tx_curr_desc ) ); adapter->tx_head = ( adapter->tx_head + 1 ) % NUM_TX_DESC; } /* Process received packets */ while ( 1 ) { i = adapter->rx_curr; rx_curr_desc = ( void * ) ( adapter->rx_base ) + ( i * sizeof ( *adapter->rx_base ) ); rx_status = rx_curr_desc->status; DBG2 ( "Before DD Check RX_status: %#08x\n", rx_status ); if ( ! ( rx_status & E1000_RXD_STAT_DD ) ) break; if ( adapter->rx_iobuf[i] == NULL ) break; DBG ( "RCTL = %#08x\n", E1000_READ_REG ( &adapter->hw, RCTL ) ); rx_len = rx_curr_desc->length; DBG ( "Received packet, rx_curr: %d rx_status: %#08x rx_len: %d\n", i, rx_status, rx_len ); rx_err = rx_curr_desc->errors; iob_put ( adapter->rx_iobuf[i], rx_len ); if ( rx_err & E1000_RXD_ERR_FRAME_ERR_MASK ) { netdev_rx_err ( netdev, adapter->rx_iobuf[i], -EINVAL ); DBG ( "e1000_poll: Corrupted packet received!" " rx_err: %#08x\n", rx_err ); } else { /* Add this packet to the receive queue. */ netdev_rx ( netdev, adapter->rx_iobuf[i] ); } adapter->rx_iobuf[i] = NULL; memset ( rx_curr_desc, 0, sizeof ( *rx_curr_desc ) ); adapter->rx_curr = ( adapter->rx_curr + 1 ) % NUM_RX_DESC; } e1000_refill_rx_ring(adapter); }
/** * Send AoE command * * @v aoe AoE session * @ret rc Return status code * * This transmits an AoE command packet. It does not wait for a * response. */ static int aoe_send_command ( struct aoe_session *aoe ) { struct ata_command *command = aoe->command; struct io_buffer *iobuf; struct aoehdr *aoehdr; union aoecmd *aoecmd; struct aoeata *aoeata; unsigned int count; unsigned int data_out_len; unsigned int aoecmdlen; /* Fail immediately if we have no netdev to send on */ if ( ! aoe->netdev ) { aoe_done ( aoe, -ENETUNREACH ); return -ENETUNREACH; } /* If we are transmitting anything that requires a response, * start the retransmission timer. Do this before attempting * to allocate the I/O buffer, in case allocation itself * fails. */ start_timer ( &aoe->timer ); /* Calculate count and data_out_len for this subcommand */ switch ( aoe->aoe_cmd_type ) { case AOE_CMD_ATA: count = command->cb.count.native; if ( count > AOE_MAX_COUNT ) count = AOE_MAX_COUNT; data_out_len = ( command->data_out ? ( count * ATA_SECTOR_SIZE ) : 0 ); aoecmdlen = sizeof ( aoecmd->ata ); break; case AOE_CMD_CONFIG: count = 0; data_out_len = 0; aoecmdlen = sizeof ( aoecmd->cfg ); break; default: return -ENOTSUP; } /* Create outgoing I/O buffer */ iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) + aoecmdlen + data_out_len ); if ( ! iobuf ) return -ENOMEM; iob_reserve ( iobuf, ETH_HLEN ); aoehdr = iob_put ( iobuf, sizeof ( *aoehdr ) ); aoecmd = iob_put ( iobuf, aoecmdlen ); memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + aoecmdlen ) ); /* Fill AoE header */ aoehdr->ver_flags = AOE_VERSION; aoehdr->major = htons ( aoe->major ); aoehdr->minor = aoe->minor; aoehdr->command = aoe->aoe_cmd_type; aoehdr->tag = htonl ( ++aoe->tag ); /* Fill AoE payload */ switch ( aoe->aoe_cmd_type ) { case AOE_CMD_ATA: /* Fill AoE command */ aoeata = &aoecmd->ata; linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ ); aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 )| ( command->cb.device & ATA_DEV_SLAVE ) | ( data_out_len ? AOE_FL_WRITE : 0 ) ); aoeata->err_feat = command->cb.err_feat.bytes.cur; aoeata->count = count; aoeata->cmd_stat = command->cb.cmd_stat; aoeata->lba.u64 = cpu_to_le64 ( command->cb.lba.native ); if ( ! command->cb.lba48 ) aoeata->lba.bytes[3] |= ( command->cb.device & ATA_DEV_MASK ); /* Fill data payload */ copy_from_user ( iob_put ( iobuf, data_out_len ), command->data_out, aoe->command_offset, data_out_len ); break; case AOE_CMD_CONFIG: /* Nothing to do */ break; default: assert ( 0 ); } /* Send packet */ return net_tx ( iobuf, aoe->netdev, &aoe_protocol, aoe->target ); }
/** * UDP WRITE * * @v pxenv_udp_write Pointer to a struct s_PXENV_UDP_WRITE * @v s_PXENV_UDP_WRITE::ip Destination IP address * @v s_PXENV_UDP_WRITE::gw Relay agent IP address, or 0.0.0.0 * @v s_PXENV_UDP_WRITE::src_port Source UDP port, or 0 * @v s_PXENV_UDP_WRITE::dst_port Destination UDP port * @v s_PXENV_UDP_WRITE::buffer_size Length of the UDP payload * @v s_PXENV_UDP_WRITE::buffer Address of the UDP payload * @ret #PXENV_EXIT_SUCCESS Packet was transmitted successfully * @ret #PXENV_EXIT_FAILURE Packet could not be transmitted * @ret s_PXENV_UDP_WRITE::Status PXE status code * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet * * Transmits a single UDP packet. A valid IP and UDP header will be * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer * should not contain precomputed IP and UDP headers, nor should it * contain space allocated for these headers. The first byte of the * buffer will be transmitted as the first byte following the UDP * header. * * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take * place. See the relevant @ref pxe_routing "implementation note" for * more details. * * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used. * * You must have opened a UDP connection with pxenv_udp_open() before * calling pxenv_udp_write(). * * On x86, you must set the s_PXE::StatusCallout field to a nonzero * value before calling this function in protected mode. You cannot * call this function with a 32-bit stack segment. (See the relevant * @ref pxe_x86_pmode16 "implementation note" for more details.) * * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw * parameter. * */ PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { struct sockaddr_in dest; struct xfer_metadata meta = { .src = ( struct sockaddr * ) &pxe_udp.local, .dest = ( struct sockaddr * ) &dest, .netdev = pxe_netdev, }; size_t len; struct io_buffer *iobuf; userptr_t buffer; int rc; DBG ( "PXENV_UDP_WRITE" ); /* Construct destination socket address */ memset ( &dest, 0, sizeof ( dest ) ); dest.sin_family = AF_INET; dest.sin_addr.s_addr = pxenv_udp_write->ip; dest.sin_port = pxenv_udp_write->dst_port; /* Set local (source) port. PXE spec says source port is 2069 * if not specified. Really, this ought to be set at UDP open * time but hey, we didn't design this API. */ pxe_udp.local.sin_port = pxenv_udp_write->src_port; if ( ! pxe_udp.local.sin_port ) pxe_udp.local.sin_port = htons ( 2069 ); /* FIXME: we ignore the gateway specified, since we're * confident of being able to do our own routing. We should * probably allow for multiple gateways. */ /* Allocate and fill data buffer */ len = pxenv_udp_write->buffer_size; iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len ); if ( ! iobuf ) { DBG ( " out of memory\n" ); pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES; return PXENV_EXIT_FAILURE; } buffer = real_to_user ( pxenv_udp_write->buffer.segment, pxenv_udp_write->buffer.offset ); copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len ); DBG ( " %04x:%04x+%x %d->%s:%d\n", pxenv_udp_write->buffer.segment, pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size, ntohs ( pxenv_udp_write->src_port ), inet_ntoa ( dest.sin_addr ), ntohs ( pxenv_udp_write->dst_port ) ); /* Transmit packet */ if ( ( rc = xfer_deliver ( &pxe_udp.xfer, iobuf, &meta ) ) != 0 ) { DBG ( "PXENV_UDP_WRITE could not transmit: %s\n", strerror ( rc ) ); pxenv_udp_write->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } pxenv_udp_write->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; } /** * UDP READ * * @v pxenv_udp_read Pointer to a struct s_PXENV_UDP_READ * @v s_PXENV_UDP_READ::dest_ip Destination IP address, or 0.0.0.0 * @v s_PXENV_UDP_READ::d_port Destination UDP port, or 0 * @v s_PXENV_UDP_READ::buffer_size Size of the UDP payload buffer * @v s_PXENV_UDP_READ::buffer Address of the UDP payload buffer * @ret #PXENV_EXIT_SUCCESS A packet has been received * @ret #PXENV_EXIT_FAILURE No packet has been received * @ret s_PXENV_UDP_READ::Status PXE status code * @ret s_PXENV_UDP_READ::src_ip Source IP address * @ret s_PXENV_UDP_READ::dest_ip Destination IP address * @ret s_PXENV_UDP_READ::s_port Source UDP port * @ret s_PXENV_UDP_READ::d_port Destination UDP port * @ret s_PXENV_UDP_READ::buffer_size Length of UDP payload * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open * @err #PXENV_STATUS_FAILURE No packet was ready to read * * Receive a single UDP packet. This is a non-blocking call; if no * packet is ready to read, the call will return instantly with * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE. * * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to * any IP address will be accepted and may be returned to the caller. * * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP * port will be accepted and may be returned to the caller. * * You must have opened a UDP connection with pxenv_udp_open() before * calling pxenv_udp_read(). * * On x86, you must set the s_PXE::StatusCallout field to a nonzero * value before calling this function in protected mode. You cannot * call this function with a 32-bit stack segment. (See the relevant * @ref pxe_x86_pmode16 "implementation note" for more details.) * * @note The PXE specification (version 2.1) does not state that we * should fill in s_PXENV_UDP_READ::dest_ip and * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program * expects us to do so, and will fail if we don't. * */ PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip }; struct in_addr dest_ip; uint16_t d_port_wanted = pxenv_udp_read->d_port; uint16_t d_port; /* Try receiving a packet */ pxe_udp.pxenv_udp_read = pxenv_udp_read; step(); if ( pxe_udp.pxenv_udp_read ) { /* No packet received */ DBG2 ( "PXENV_UDP_READ\n" ); pxe_udp.pxenv_udp_read = NULL; goto no_packet; } dest_ip.s_addr = pxenv_udp_read->dest_ip; d_port = pxenv_udp_read->d_port; DBG ( "PXENV_UDP_READ" ); /* Filter on destination address and/or port */ if ( dest_ip_wanted.s_addr && ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) { DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) ); DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) ); goto no_packet; } if ( d_port_wanted && ( d_port_wanted != d_port ) ) { DBG ( " wrong port %d", htons ( d_port ) ); DBG ( " (wanted %d)\n", htons ( d_port_wanted ) ); goto no_packet; } DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment, pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size, inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) )); DBG ( "%d<-%s:%d\n", ntohs ( pxenv_udp_read->s_port ), inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ), ntohs ( pxenv_udp_read->d_port ) ); pxenv_udp_read->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; no_packet: pxenv_udp_read->Status = PXENV_STATUS_FAILURE; return PXENV_EXIT_FAILURE; }
/** * Decrypt a packet using CCMP * * @v crypto CCMP cryptosystem * @v eiob I/O buffer containing encrypted packet * @ret iob I/O buffer containing cleartext packet */ static struct io_buffer * ccmp_decrypt ( struct net80211_crypto *crypto, struct io_buffer *eiob ) { struct ccmp_ctx *ctx = crypto->priv; struct ieee80211_frame *hdr; struct io_buffer *iob; const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; int datalen = iob_len ( eiob ) - hdrlen - CCMP_HEAD_LEN - CCMP_MIC_LEN; struct ccmp_head *head; struct ccmp_nonce nonce; struct ccmp_aad aad; u8 rx_pn[6], their_mic[8], our_mic[8]; iob = alloc_iob ( hdrlen + datalen ); if ( ! iob ) return NULL; /* Copy frame header */ memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen ); hdr = iob->data; hdr->fc &= ~IEEE80211_FC_PROTECTED; /* Check and update RX packet number */ head = eiob->data + hdrlen; memcpy ( rx_pn, head->pn_lo, 2 ); memcpy ( rx_pn + 2, head->pn_hi, 4 ); if ( pn_to_u64 ( rx_pn ) <= ctx->rx_seq ) { DBGC ( ctx, "WPA-CCMP %p: packet received out of order " "(%012llx <= %012llx)\n", ctx, pn_to_u64 ( rx_pn ), ctx->rx_seq ); free_iob ( iob ); return NULL; } ctx->rx_seq = pn_to_u64 ( rx_pn ); DBGC2 ( ctx, "WPA-CCMP %p: RX packet number %012llx\n", ctx, ctx->rx_seq ); /* Form nonce */ nonce.prio = 0; memcpy ( nonce.a2, hdr->addr2, ETH_ALEN ); u64_to_pn ( ctx->rx_seq, nonce.pn, PN_MSB ); /* Form additional authentication data */ aad.fc = ( hdr->fc & CCMP_AAD_FC_MASK ) | IEEE80211_FC_PROTECTED; memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */ aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK; /* Copy-decrypt data and MIC */ ccmp_ctr_xor ( ctx, &nonce, eiob->data + hdrlen + sizeof ( *head ), iob_put ( iob, datalen ), datalen, eiob->tail - CCMP_MIC_LEN, their_mic ); /* Check MIC */ ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, our_mic ); if ( memcmp ( their_mic, our_mic, CCMP_MIC_LEN ) != 0 ) { DBGC2 ( ctx, "WPA-CCMP %p: MIC failure\n", ctx ); free_iob ( iob ); return NULL; } DBGC2 ( ctx, "WPA-CCMP %p: decrypted packet %p -> %p\n", ctx, eiob, iob ); return iob; }