예제 #1
0
/*!
 * \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;
}
예제 #2
0
/*
 * 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);
    }
}
예제 #3
0
/*
 * 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;
}
예제 #4
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);
}
/*!
 * \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;
}
예제 #6
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);
}
예제 #7
0
/*
 * 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);
    }
}
예제 #8
0
/*!
 * \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);
}
예제 #9
0
/*!
 * \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;
}
예제 #10
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;
            }
        }