예제 #1
0
/*!
 * \brief Send a UDP packet.
 *
 * \param sock  Socket descriptor. This pointer must have been
 *              retrieved by calling NutUdpCreateSocket().
 * \param daddr IP address of the remote host in network byte order.
 * \param port  Remote port number in host byte order.
 * \param nb    Network buffer structure containing the datagram.
 *              This buffer will be released if the function returns
 *              an error.
 *
 * \note Applications typically do not call this function but
 *       use the UDP socket interface.
 *
 * \return 0 on success, -1 otherwise.
 */
int NutUdpOutput(UDPSOCKET * sock, uint32_t daddr, uint16_t port, NETBUF * nb)
{
    uint32_t saddr;
    uint32_t csum;
    UDPHDR *uh;
    NUTDEVICE *dev;
    IFNET *nif;

    if ((nb = NutNetBufAlloc(nb, NBAF_TRANSPORT, sizeof(UDPHDR))) == 0)
        return -1;

    uh = nb->nb_tp.vp;
    uh->uh_sport = sock->so_local_port;
    uh->uh_dport = htons(port);
    uh->uh_ulen = htons((uint16_t)(nb->nb_tp.sz + nb->nb_ap.sz));

    /*
     * Get local address for this destination.
     */
    if ((dev = NutIpRouteQuery(daddr, &saddr)) != 0) {
        nif = dev->dev_icb;
        saddr = nif->if_local_ip;
    } else
        saddr = 0;

    uh->uh_sum = 0;
    csum = NutIpPseudoChkSumPartial(saddr, daddr, IPPROTO_UDP, uh->uh_ulen);
    csum = NutIpChkSumPartial(csum, uh, sizeof(UDPHDR));
    uh->uh_sum = NutIpChkSum(csum, nb->nb_ap.vp, nb->nb_ap.sz);

    return NutIpOutput(IPPROTO_UDP, daddr, nb);
}
예제 #2
0
/*!
 * \brief Send an ICMP datagram.
 *
 * Known ICMP types are:
 *
 * - #ICMP_ECHOREPLY Echo reply
 * - #ICMP_UNREACH Destination unreachable
 * - #ICMP_SOURCEQUENCH Packet lost
 * - #ICMP_REDIRECT Shorter route
 * - #ICMP_ECHO Echo reply
 * - #ICMP_ROUTERADVERT Router advertisement
 * - #ICMP_ROUTERSOLICIT Router solicitation
 * - #ICMP_TIMXCEED Time exceeded
 * - #ICMP_PARAMPROB Bad IP header
 * - #ICMP_TSTAMP Timestamp request
 * - #ICMP_TSTAMPREPLY Timestamp reply
 * - #ICMP_IREQ Information request
 * - #ICMP_IREQREPLY Information reply
 * - #ICMP_MASKREQ Address mask request
 * - #ICMP_MASKREPLY Address mask reply
 *
 * \param type Type of the ICMP message.
 * \param dest Destination IP address.
 * \param nb   Network buffer structure containing the datagram.
 *
 * \return 0 on success, -1 otherwise.
 */
int NutIcmpOutput(uint8_t type, uint32_t dest, NETBUF * nb)
{
    ICMPHDR *icp;
    uint16_t csum;

    icp = (ICMPHDR *) nb->nb_tp.vp;
    icp->icmp_type = type;
    icp->icmp_cksum = 0;

    csum = NutIpChkSumPartial(0, nb->nb_tp.vp, nb->nb_tp.sz);
    icp->icmp_cksum = NutIpChkSum(csum, nb->nb_ap.vp, nb->nb_ap.sz);

    return NutIpOutput(IPPROTO_ICMP, dest, nb);
}
/*!
 * \brief Initiate TCP segment transmission.
 *
 * Check the TCP socket status and send any segment waiting
 * for transmission.
 *
 * The function will not return until the data has been stored in the 
 * network device hardware for transmission. If the device is not ready 
 * for transmitting a new packet, the calling thread will be suspended 
 * until the device becomes ready again. 
 *
 * If the target host is connected through an Ethernet network and if 
 * the hardware address of that host is currently unknown, an ARP 
 * request is sent out and the function will block until a response 
 * is received or an ARP timeout occurs.
 *
 * Segments containing data or SYN and FIN flags are added to a special 
 * queue for unacknowledged segments and will be retransmitted by the 
 * TCP timer thread, if not acknowledged by the remote within a specific 
 * time. The state machine will remove these segments from the queue
 * as soon as they are acknowledged.
 *
 * \note This function is mainly used by the TCP state machine.
 *       Applications typically do not call this function but 
 *       use NutTcpSend(), which is part of the TCP socket interface.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved 
 *              by calling NutTcpCreateSocket().
 * \param data  Pointer to TCP segment contents.
 * \param size  TCP segment length.
 *
 * \return 0 on success, -1 otherwise. Returning 0 does not imply that 
 *         the data has been successfully delivered, because flow control 
 *         and retransmission is still handled in the background.
 */
int NutTcpOutput(TCPSOCKET * sock, CONST u_char * data, u_short size)
{
    NETBUF *nb;
    NETBUF *nb_clone = 0;
    TCPHDR *th;
    u_short csum;
    u_char hlen;

    /*
     * Check if anything to send at all.
     */
    if (size == 0
        && (sock->so_tx_flags & (SO_SYN | SO_FIN | SO_FORCE)) == 0)
        return 0;

    /*
     * Build TCP header. Add room for MAXSEG option if this is a
     * SYN segment.
     */
    hlen = sizeof(TCPHDR);
    if (sock->so_tx_flags & SO_SYN)
        hlen += 4;
    if ((nb = NutNetBufAlloc(0, NBAF_TRANSPORT, hlen)) == 0) {
        sock->so_last_error = ENOBUFS;
        return -1;
    }
    th = (TCPHDR *) nb->nb_tp.vp;
    th->th_sport = sock->so_local_port;
    th->th_dport = sock->so_remote_port;
    th->th_x2 = 0;
    th->th_off = hlen >> 2;

    sock->so_tx_flags &= ~SO_FORCE;

    /*
     * Process ACK flag.
     */
    th->th_seq = htonl(sock->so_tx_nxt);
    if (sock->so_tx_flags & SO_ACK) {
        th->th_flags = TH_ACK;
        sock->so_tx_flags &= ~SO_ACK;
        th->th_ack = htonl(sock->so_rx_nxt);
    } else {
        th->th_flags = 0;
        th->th_ack = 0;
    }

    /*
     * Any SYN is sent first. Add options too. We rely on the caller 
     * not to send a SYN segment with data, because this may break
     * some old stacks.
     */
    if (sock->so_tx_flags & SO_SYN) {
        u_char *cp;
        u_short n_mss = htons(sock->so_mss);

        th->th_flags |= TH_SYN;
        sock->so_tx_flags &= ~SO_SYN;
        sock->so_tx_nxt++;

        cp = (u_char *) (th + 1);
        *cp++ = TCPOPT_MAXSEG;
        *cp++ = TCPOLEN_MAXSEG;
        *cp++ = *(u_char *)&n_mss;
        *cp = *((u_char *)(&n_mss) + 1);
    }

    /*
     * Next preference is sending data. Set PUSH flag.
     */
    else if (size) {
        if ((nb = NutNetBufAlloc(nb, NBAF_APPLICATION, size)) == 0) {
            sock->so_last_error = ENOBUFS;
            return -1;
        }
        memcpy(nb->nb_ap.vp, (void *)data, size);
        sock->so_tx_nxt += size;
        th->th_flags |= TH_PUSH;
    }

    /*
     * If all data sent, transmit any waiting FIN.
     */
    else if (sock->so_tx_flags & SO_FIN) {
        th->th_flags |= TH_FIN;
        sock->so_tx_flags &= ~SO_FIN;
        sock->so_tx_nxt++;
        //@@@printf ("[%04X]TcpOutput: sending FIN\n", (u_short) sock);
    }

    /*
     * We close our receiver window, if it is
     * below the maximum segment size.
     */
    if (sock->so_rx_win < sock->so_mss)
        th->th_win = 0;
    else
        th->th_win = htons(sock->so_rx_win);
    th->th_sum = 0;
    th->th_urp = 0;

    /*
     * Calculate TCP checksum.
     */
    csum =
        NutIpPseudoChkSumPartial(sock->so_local_addr, sock->so_remote_addr,
                                 IPPROTO_TCP,
                                 htons(nb->nb_tp.sz + nb->nb_ap.sz));
    csum = NutIpChkSumPartial(csum, th, nb->nb_tp.sz);
    th->th_sum = NutIpChkSum(csum, nb->nb_ap.vp, nb->nb_ap.sz);

#ifdef NUTDEBUG
    if (__tcp_trf)
        NutDumpTcpHeader(__tcp_trs, "OUT", sock, nb);
#endif

	/*
	 * To avoid a race condition in tcp state machine, the segment is first
     * appended to the transmission que, and then sent to the network.
	 */

    /*
     * Append the segment to our transmission queue.
     */
    //@@@printf ("[%04X]TcpOutput: size: %u, flags: %u\n", (u_short) sock, size, th->th_flags);
    if (size || ((th->th_flags & (TH_FIN | TH_SYN)))) {
        //@@@printf ("[%04X]TcpOutput: appending nb to queue\n", (u_short) sock);
        NETBUF *nbp;

        nb->nb_next = 0;
        if ((nbp = sock->so_tx_nbq) == 0) {
            sock->so_tx_nbq = nb;
            /*
             * First entry, so initialize our retransmission timer.
             * It is also set at various places in the state machine,
             * but here is the best central point to do it. We may
             * carefully check later, if we can remove some in the
             * state machine.
             */
            sock->so_retran_time = (u_short) NutGetMillis() | 1;
        }
        else {
            while (nbp->nb_next)
                nbp = nbp->nb_next;
            nbp->nb_next = nb;
        }
        if (sock->so_rtt_seq == 0)
            sock->so_rtt_seq = ntohl (th->th_seq);
        nb_clone = NutNetBufClone (nb);
    }
    else
        nb_clone = nb;

    /*
     * IP output might fail because of routing, ARP or network device 
     * problems or because the system ran out of memory.
     */
    if (NutIpOutput(IPPROTO_TCP, sock->so_remote_addr, nb_clone)) 
        return -1;

    NutNetBufFree (nb_clone);
    return 0;
}