/*! * \brief Allocate an ARP network buffer structure. * * \param type Type of ARP packet. * \param ip Target IP address. * \param mac Target MAC address, null pointer for broadcast. * * \return Pointer to the allocated network buffer structure * or 0 on failure. */ NETBUF *NutArpAllocNetBuf(uint16_t type, uint32_t ip, uint8_t * mac) { NETBUF *nb; ETHERARP *ea; ARPHDR *ah; if ((nb = NutNetBufAlloc(0, NBAF_NETWORK, sizeof(ETHERARP))) == 0) return 0; ea = nb->nb_nw.vp; ah = (ARPHDR *) & ea->ea_hdr; /* * Set ARP header. */ ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = 6; ah->ar_pln = 4; ah->ar_op = htons(type); /* * Set ARP destination data. */ if (mac) memcpy(ea->arp_tha, mac, 6); else memset(ea->arp_tha, 0xff, 6); ea->arp_tpa = ip; return nb; }
/* * Send a Configure-Request. */ void PapTxAuthReq(NUTDEVICE *dev, uint8_t id) { PPPDCB *dcb = dev->dev_dcb; NETBUF *nb; char *cp; int len; /* * Create the request. */ len = 2; if(dcb->dcb_user) len += strlen((char*)dcb->dcb_user); if(dcb->dcb_pass) len += strlen((char*)dcb->dcb_pass); if ((nb = NutNetBufAlloc(0, NBAF_APPLICATION, len)) != 0) { cp = nb->nb_ap.vp; *cp = dcb->dcb_user ? (char)strlen((char*)dcb->dcb_user) : 0; if(*cp) memcpy(cp + 1, dcb->dcb_user, *cp); cp += *cp + 1; *cp = dcb->dcb_pass ? (char)strlen((char*)dcb->dcb_pass) : 0; if(*cp) memcpy(cp + 1, dcb->dcb_pass, *cp); dcb->dcb_auth_state = PAPCS_AUTHREQ; NutPapOutput(dev, XCP_CONFREQ, ++dcb->dcb_reqid, nb); } }
/* * This function will send an ICMP echo request to the specified destionation. */ static int IcmpSendPing(uint32_t dest, uint16_t id, uint16_t seq, int len) { NETBUF *nb; int i; uint8_t *dp; uint32_t spec; /* Create a new NETBUF. */ nb = NutNetBufAlloc(NULL, NBAF_APPLICATION, len); if (nb) { /* Fill the data area with sample characters. */ dp = (uint8_t *) nb->nb_ap.vp; for (i = 0; i < len; i++) { *dp++ = 'a' + (i & 15); } /* Set up the echo request ID and sequence number. */ spec = id; spec <<= 16; spec |= seq; /* Send out the packet. The name of this function is misleading. */ if (NutIcmpReply(ICMP_ECHO, 0, htonl(spec), dest, nb) == 0) { /* Funny Nut/Net expects us to release the packet only if it has been successfully processed. */ NutNetBufFree(nb); return 0; } } /* Return an error. */ return -1; }
/*! * \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 Release a network buffer structure. * * Returns all memory previously allocated by a * network buffer to the available heap space. * * \param nb Points to an existing network buffer * structure, previously allocated by * NutNetBufAlloc(). * * \return 0 if successfull or -1 if the structure * contains previously released memory space. */ int NutNetBufFree(NETBUF * nb) { if(nb) { NutNetBufAlloc(nb, nb->nb_flags, 0); if (NutHeapFree(nb)) { return -1; } } return 0; }
/*! * \brief Send an ICMP message to a given destination. * * \param type Type of the ICMP message. See NutIcmpOutput() for * a list of valid types. * \param code Type subcode. * \param spec Type specific data item. * \param dest IP address of the target. * \param nb Network buffer structure containing the message to be sent. * The structure must have been allocated by a previous * call NutNetBufAlloc() and will be freed if this function * returns with an error. * * \return 0 on success, -1 otherwise. */ int NutIcmpReply(uint8_t type, uint8_t code, uint32_t spec, uint32_t dest, NETBUF * nb) { ICMPHDR *icp; if ((nb = NutNetBufAlloc(nb, NBAF_TRANSPORT, sizeof(ICMPHDR))) == 0) return -1; icp = (ICMPHDR *) nb->nb_tp.vp; icp->icmp_code = code; icp->icmp_spec = spec; return NutIcmpOutput(type, dest, nb); }
/* * Send a Protocol-Reject for some protocol. */ void LcpTxProtRej(NUTDEVICE * dev, uint16_t protocol, NETBUF * nb) { PPPDCB *dcb = dev->dev_dcb; NETBUF *nbr; uint16_t *sp; if ((nbr = NutNetBufAlloc(0, NBAF_APPLICATION, nb->nb_nw.sz)) != 0) { sp = nbr->nb_ap.vp; *sp++ = htons(protocol); memcpy(sp, nb->nb_nw.vp, nb->nb_nw.sz - 2); NutNetBufFree(nb); NutLcpOutput(dev, LCP_PROTREJ, ++dcb->dcb_rejid, nbr); } else { NutNetBufFree(nb); } }
/*! * \brief Send an ICMP message as a response to a given destination. * * \param type Type of the ICMP message. See NutIcmpOutput() for * a list of valid types. * \param code Type subcode. * \param spec Type specific data item. * \param nb Network buffer structure containing the previously recevied * network packet. According to RFC792 the complete ip header * and the first 8 bytes of the transport netbuf is used as the * application data for the response. If this function returns * with an error, the buffer is freed. The destination addess is * taken from the ip header. * * \return 0 on success, -1 otherwise. */ int NutIcmpResponse(uint8_t type, uint8_t code, uint32_t spec, NETBUF * nb) { IPHDR *ip; uint32_t dest; ip = nb->nb_nw.vp; dest = ip->ip_src; if ((nb = NutNetBufAlloc(nb, NBAF_APPLICATION, sizeof(IPHDR) + 8)) == 0) return -1; memcpy(nb->nb_ap.vp, nb->nb_nw.vp, sizeof(IPHDR)); memcpy((char *)nb->nb_ap.vp + sizeof(IPHDR), nb->nb_tp.vp, 8); return NutIcmpReply(type, code, spec, dest, nb); }
/*! * \brief Send a PAP packet. * * \note Applications typically do not call this function. * * \param dev Identifies the device to use. * \param code Type subcode. * \param id Exchange identifier. * \param nb Network buffer structure containing the packet to send * or null if the packet contains no information. * The structure must have been allocated by a previous * call NutNetBufAlloc() and will be freed when this function * returns. * * \return 0 on success, -1 in case of any errors. */ int NutPapOutput(NUTDEVICE * dev, uint8_t code, uint8_t id, NETBUF * nb) { XCPHDR *xch; if ((nb = NutNetBufAlloc(nb, NBAF_NETWORK, sizeof(XCPHDR))) == 0) return -1; xch = nb->nb_nw.vp; xch->xch_code = code; xch->xch_id = id; xch->xch_len = htons(nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz); if (NutPppOutput(dev, PPP_PAP, 0, nb) == 0) NutNetBufFree(nb); return 0; }
/* * Send a Configure-Request. */ void LcpTxConfReq(NUTDEVICE * dev, uint8_t id, uint8_t rejected) { PPPDCB *dcb = dev->dev_dcb; XCPOPT *xcpo; NETBUF *nb; /* * Not currently negotiating, reset options. */ if (dcb->dcb_lcp_state != PPPS_REQSENT && dcb->dcb_lcp_state != PPPS_ACKRCVD && dcb->dcb_lcp_state != PPPS_ACKSENT) { LcpResetOptions(dev); dcb->dcb_lcp_naks = 0; } dcb->dcb_acked = 0; dcb->dcb_retries = 0; /* * Create the request. */ if ((nb = NutNetBufAlloc(0, NBAF_APPLICATION, rejected ? 6 : 12)) != 0) { xcpo = nb->nb_ap.vp; xcpo->xcpo_type = LCP_ASYNCMAP; xcpo->xcpo_len = 6; xcpo->xcpo_.ul = htonl(LCP_DEFOPT_ASYNCMAP); /* Should this be "= 0;" instead? */ /* * This is a temporary hack. In the initial version * we sent the ASYNCMAP only and never expected any * rejects. The MAGICNUMBER had been added later * to support echo requests, but some servers reject * this option. Now we still do not provide full * reject processing but blindly assume, that the * MAGICNUMBER is the rejected option. */ if (!rejected) { xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len); xcpo->xcpo_type = LCP_MAGICNUMBER; xcpo->xcpo_len = 6; xcpo->xcpo_.ul = dcb->dcb_neg_magic; } NutLcpOutput(dev, XCP_CONFREQ, id, 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; }
/*! \fn AhdlcRx(void *arg) * \brief Asynchronous HDLC receiver thread. * * * Running at high priority. */ THREAD(AhdlcRx, arg) { NUTDEVICE *dev = arg; NUTDEVICE *netdev; AHDLCDCB *dcb = dev->dev_dcb; IFNET *ifn; NETBUF *nb; uint8_t *rxbuf; uint8_t *rxptr; uint16_t rxcnt; uint8_t ch; uint16_t tbx; uint8_t inframe; uint8_t escaped; uint16_t rxfcs; NutThreadSetPriority(9); for (;;) { /* * Reset variables to their initial state */ rxptr = 0; rxcnt = 0; escaped = 0; rxfcs = AHDLC_INITFCS; inframe = 0; for (;;) { /* * Wait until the network interface has been attached. * This will be initiated by the application calling * NutNetIfConfig(), which in turn calls a HDLC_SETIFNET * ioctl() to store the NUTDEVICE pointer of the network * device in dev_icb and trigger an event on dcb_mf_evt. */ while ((netdev = dev->dev_icb) == 0) { if (NutEventWait(&dcb->dcb_mf_evt, 1000) == 0) { NutSleep(100); } } ifn = netdev->dev_icb; dcb->dcb_rtimeout = 1000; inframe = 0; /* * Allocate the receive buffer, if this fails, we are in a * low memory situation. Take a nap and see, if the * situation improved. */ if ((rxbuf = NutHeapAlloc(dcb->dcb_rx_mru)) != 0) { break; } NutSleep(1000); } /* * Signal the link driver that we are up. */ ifn->if_send = AhdlcOutput; netdev->dev_ioctl(netdev, LCP_LOWERUP, 0); for (;;) { /* * If we are still connected to a network, fetch the next * character from the buffer. */ while (dcb->dcb_rd_idx == dcb->dcb_rx_idx) { if (dev->dev_icb == 0) break; // TODO: Check for idle timeout. if (NutEventWait(&dcb->dcb_rx_rdy, dcb->dcb_rtimeout)) { continue; } } /* * Leave loop if network interface is detached */ if (dev->dev_icb == 0) break; /* * If RAW mode is active, we are not allowing any data encapsulation * processing. So we just sleep for a while. */ if (dcb->dcb_modeflags & UART_MF_RAWMODE) { /* * It is a must to sleep here, because if we just yield it could create * too much processing in here and stall processing elsewhere. This gives * opportunity to other threads to process incoming data from USART. */ NutSleep(100); continue; } /* * Read next character from input buffer */ ch = dcb->dcb_rx_buf[dcb->dcb_rd_idx++]; if (inframe) { if (ch != AHDLC_FLAG) { if (ch == AHDLC_ESCAPE) { escaped = 1; continue; } if (escaped) { ch ^= AHDLC_TRANS; escaped = 0; } /* * Unless the peer lied to us about the negotiated MRU, * we should never get a frame which is too long. If it * happens, toss it away and grab the next incoming one. */ if (rxcnt++ < dcb->dcb_rx_mru) { /* Update calculated checksum and store character in buffer. */ tbx = (uint16_t) ((uint8_t) rxfcs ^ ch) << 1; rxfcs >>= 8; rxfcs ^= ((uint16_t) PRG_RDB(fcstab + tbx) << 8) | PRG_RDB(fcstab + tbx + 1); *rxptr++ = ch; } else inframe = 0; continue; } if (rxcnt > 6 && rxfcs == AHDLC_GOODFCS) { /* * If the frame checksum is valid, create a NETBUF * and pass it to the network specific receive handler. */ rxcnt -= 2; if ((nb = NutNetBufAlloc(0, NBAF_DATALINK, rxcnt)) != 0) { memcpy(nb->nb_dl.vp, rxbuf, rxcnt); (*ifn->if_recv) (netdev, nb); } } } /* * If frame flag is received, resync frame processing */ if (ch == AHDLC_FLAG) { inframe = 1; escaped = 0; rxptr = rxbuf; rxcnt = 0; rxfcs = AHDLC_INITFCS; } }