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)); }
/** * 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; }
/** * 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; }
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 ); }
/** * 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; }
/** * 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; }
/** * 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 ); }