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