Beispiel #1
0
/*
 * Configure-Nak or Configure-Reject received.
 */
static void LcpRxConfNakRej(NUTDEVICE * dev, uint8_t id, NETBUF * nb, uint8_t rejected)
{
    PPPDCB *dcb = dev->dev_dcb;

    XCPOPT *xcpo;
    uint16_t xcpl;

    /*
     * Ignore, if we are not expecting this id.
     */
    if (id != dcb->dcb_reqid || dcb->dcb_acked) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * TODO: Process acked options.
     */
    dcb->dcb_acked = 1;

    xcpo = nb->nb_ap.vp;
    xcpl = nb->nb_ap.sz;
    while (xcpl >= 2) {
        xcpl -= xcpo->xcpo_len;
        xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
    }

    NutNetBufFree(nb);

    switch (dcb->dcb_lcp_state) {
    case PPPS_CLOSED:
    case PPPS_STOPPED:
        /*
         * Go away, we're closed. 
         */
        NutLcpOutput(dev, XCP_TERMACK, id, 0);
        break;

    case PPPS_REQSENT:
    case PPPS_ACKSENT:
        /* They didn't agree to what we wanted - try another request */
        LcpTxConfReq(dev, ++dcb->dcb_reqid, rejected);
        break;

    case PPPS_ACKRCVD:
        /* Got a Nak/reject when we had already had an Ack?? oh well... */
        LcpTxConfReq(dev, ++dcb->dcb_reqid, 0);
        dcb->dcb_lcp_state = PPPS_REQSENT;
        break;

    case PPPS_OPENED:
        /* 
         * Go down and restart negotiation.
         */
        IpcpLowerDown(dev);
        LcpTxConfReq(dev, ++dcb->dcb_reqid, 0);
        dcb->dcb_lcp_state = PPPS_REQSENT;
        break;
    }
}
/*!
 * \brief Handle incoming ICMP packets.
 *
 * Incoming ICMP packets are processed in the background.
 * NutNet currently handles echo request and destination
 * unreachable packets. Any other packet type is silently 
 * discarded.
 *
 * \note This routine is called by the IP layer on incoming 
 *       ICMP datagrams. Applications typically do not call 
 *       this function.
 *
 * \param dev Identifies the device that received the packet.
 * \param nb  Pointer to a network buffer structure containing 
 *            the ICMP datagram.
 */
void NutIcmpInput(NUTDEVICE * dev, NETBUF * nb)
{
    /*
     * Silently discard packets, which are too small.
     */
    if (nb->nb_tp.sz > ICMP_MINLEN) {
        ICMPHDR *icp = (ICMPHDR *) nb->nb_tp.vp;

        if (nb->nb_tp.sz > sizeof(ICMPHDR)) {
            nb->nb_ap.sz = nb->nb_tp.sz - sizeof(ICMPHDR);
            nb->nb_ap.vp = ((u_char *) icp) + sizeof(ICMPHDR);
            nb->nb_tp.sz = sizeof(ICMPHDR);
        }

        switch (icp->icmp_type) {
        case ICMP_ECHO:
            if (NutIcmpReflect(dev, ICMP_ECHOREPLY, nb) == 0)
                NutNetBufFree(nb);
            break;
        case ICMP_UNREACH:
            if (NutIcmpUnreach(nb) != 0)
                NutNetBufFree(nb);
            break;
        default:
            NutNetBufFree(nb);
        }
    } else
        NutNetBufFree(nb);
}
 /* @@@ 2003-10-24: modified by OS for udp packet queue */
void NutUdpInput(NETBUF * nb, ureg_t bcast)
{
    UDPHDR *uh;
    UDPSOCKET *sock;

    uh = (UDPHDR *) nb->nb_tp.vp;

    nb->nb_ap.vp = uh + 1;
    nb->nb_ap.sz = nb->nb_tp.sz - sizeof(UDPHDR);
    nb->nb_tp.sz = sizeof(UDPHDR);

    /*
     * Find a port. If none exists and if this datagram hasn't been
     * broadcasted, return an ICMP unreachable.
     */
    if ((sock = NutUdpFindSocket(uh->uh_dport)) == 0) {
        if (bcast || NutIcmpResponse(ICMP_UNREACH, ICMP_UNREACH_PORT, 0, nb) == 0) {
        	NutNetBufFree(nb);
        }
        return;
    }

    /* if buffer size is defined, use packet queue */
    if (sock->so_rx_bsz) {
        /* New packet fits into the buffer? */
        if (sock->so_rx_cnt + nb->nb_ap.sz > sock->so_rx_bsz) {
            /* No, so discard it */
            NutNetBufFree(nb);
            return;
        } else {
            /* if a first packet is already in the queue, find the end
             * and add the new packet */
            if (sock->so_rx_nb) {
                NETBUF *snb;
                for (snb = sock->so_rx_nb; snb->nb_next != 0; snb = snb->nb_next);
                snb->nb_next = nb;
            } else
                sock->so_rx_nb = nb;

            /* increment input buffer count */
            sock->so_rx_cnt += nb->nb_ap.sz;
        };
    } else {                    /* no packet queue */
        /* if a packet is still buffered, discard it */
        if (sock->so_rx_nb) {
            NutNetBufFree(sock->so_rx_nb);
        }
        sock->so_rx_nb = nb;
        sock->so_rx_cnt = nb->nb_ap.sz; /* set input buffer count to size of new packet */
    };

    /* post the event only, if one thread is waiting */
    if (sock->so_rx_rdy)
        NutEventPost(&sock->so_rx_rdy);
}
/*
 * Process incoming ICMP messages for destination unreachable.
 */
static int NutIcmpUnreach(NETBUF * nb)
{
    IPHDR *ih;
    TCPHDR *th;
    TCPSOCKET *sock;

    if (nb->nb_ap.sz <= sizeof(IPHDR) + 8)
        return -1;

    ih = nb->nb_ap.vp;
    if (ih->ip_p != IPPROTO_TCP)
        return -1;

    th = (TCPHDR *) ((u_char *) ih) + sizeof(IPHDR);
    sock = NutTcpFindSocket(th->th_dport, th->th_sport, ih->ip_src);
    if (sock == 0)
        return -1;

    if (sock->so_state != TCPS_SYN_SENT && sock->so_state != TCPS_ESTABLISHED)
        return -1;

    NutTcpAbortSocket(sock, EHOSTUNREACH);
    NutNetBufFree(nb);
    return 0;
}
Beispiel #5
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);
    }
}
Beispiel #6
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;
}
Beispiel #7
0
/*
 * Receive an Code-Reject.
 */
static void IpcpRxCodeRej(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;

    NutNetBufFree(nb);
    if (dcb->dcb_ipcp_state == PPPS_ACKRCVD)
        dcb->dcb_ipcp_state = PPPS_REQSENT;
}
Beispiel #8
0
/*!
 * \brief Handle incoming PAP packets.
 *
 *
 * \param dev Identifies the device that received the packet.
 * \param nb  Pointer to a network buffer structure containing 
 *            the PAP packet.
 */
void NutPapInput(NUTDEVICE * dev, NETBUF * nb)
{
    XCPHDR *pap;
    uint16_t len;

    /*
     * Drop packets with illegal lengths.
     */
    if (nb->nb_nw.sz < sizeof(XCPHDR)) {
        NutNetBufFree(nb);
        return;
    }
    pap = (XCPHDR *) nb->nb_nw.vp;
    if ((len = htons(pap->xch_len)) < sizeof(XCPHDR) || len > nb->nb_nw.sz) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * Split the PAP packet.
     */
    nb->nb_ap.vp = pap + 1;
    nb->nb_ap.sz = htons(pap->xch_len) - sizeof(XCPHDR);

    /*
     * Action depends on code.
     */
    switch (pap->xch_code) {
    case XCP_CONFREQ:
        PapRxAuthReq(dev, pap->xch_id, nb);
        break;

    case XCP_CONFACK:
        PapRxAuthAck(dev, pap->xch_id, nb);
        break;

    case XCP_CONFNAK:
        PapRxAuthNak(dev, pap->xch_id, nb);
        break;

    default:
        break;
    }
    NutNetBufFree(nb);
}
/*!
 * \brief Create a copy of an existing network buffer
 *        structure.
 *
 * \param nb Points to an existing network buffer
 *           structure, previously allocated by
 *           NutNetBufAlloc().
 *
 * \return Pointer to a newly allocated copy.
 */
NETBUF *NutNetBufClone(NETBUF * nb)
{
    NETBUF *clone;

    if ((clone = NutHeapAllocClear(sizeof(NETBUF))) == 0)
        return 0;

    if (nb->nb_dl.sz) {
        if (NutNetBufAllocData(&clone->nb_dl, nb->nb_dl.sz)) {
            NutNetBufFree(clone);
            return 0;
        }
        memcpy(clone->nb_dl.vp, nb->nb_dl.vp, nb->nb_dl.sz);
        clone->nb_flags |= NBAF_DATALINK;
    }
    if (nb->nb_nw.sz) {
        if (NutNetBufAllocData(&clone->nb_nw, nb->nb_nw.sz)) {
            NutNetBufFree(clone);
            return 0;
        }
        memcpy(clone->nb_nw.vp, nb->nb_nw.vp, nb->nb_nw.sz);
        clone->nb_flags |= NBAF_NETWORK;
    }
    if (nb->nb_tp.sz) {
        if (NutNetBufAllocData(&clone->nb_tp, nb->nb_tp.sz)) {
            NutNetBufFree(clone);
            return 0;
        }
        memcpy(clone->nb_tp.vp, nb->nb_tp.vp, nb->nb_tp.sz);
        clone->nb_flags |= NBAF_TRANSPORT;
    }
    if (nb->nb_ap.sz) {
        if (NutNetBufAllocData(&clone->nb_ap, nb->nb_ap.sz)) {
            NutNetBufFree(clone);
            return 0;
        }
        memcpy(clone->nb_ap.vp, nb->nb_ap.vp, nb->nb_ap.sz);
        clone->nb_flags |= NBAF_APPLICATION;
    }
    return clone;
}
Beispiel #10
0
/*
 * Received an Echo-Request.
 */
static void LcpRxEchoReq(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;

    if (dcb->dcb_lcp_state != PPPS_OPENED) {
        NutNetBufFree(nb);
    } else {
        /* Use local magic number. */
        memcpy(nb->nb_ap.vp, &dcb->dcb_loc_magic, sizeof(uint32_t));
        NutLcpOutput(dev, LCP_ERP, id, nb);
    }
}
Beispiel #11
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;
}
Beispiel #12
0
/*
 * \brief Terminate request received.
 */
static void IpcpRxTermReq(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;

    NutNetBufFree(nb);
    switch (dcb->dcb_ipcp_state) {
    case PPPS_ACKRCVD:
    case PPPS_ACKSENT:
        dcb->dcb_ipcp_state = PPPS_REQSENT;
        break;

    case PPPS_OPENED:
        IpcpLowerDown(dev);
        dcb->dcb_ipcp_state = PPPS_STOPPING;
        break;
    }
    NutIpcpOutput(dev, XCP_TERMACK, id, 0);
}
Beispiel #13
0
/*
 * Terminate-Ack received.
 */
static void LcpRxTermAck(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;

    NutNetBufFree(nb);

    switch (dcb->dcb_lcp_state) {
    case PPPS_CLOSING:
        dcb->dcb_lcp_state = PPPS_CLOSED;
        break;
    case PPPS_STOPPING:
        dcb->dcb_lcp_state = PPPS_STOPPED;
        break;

    case PPPS_ACKRCVD:
        dcb->dcb_lcp_state = PPPS_REQSENT;
        break;

    case PPPS_OPENED:
        IpcpLowerDown(dev);
        LcpTxConfReq(dev, ++dcb->dcb_reqid, 0);
        break;
    }
}
/*!
 * \brief Query MAC address for a specified IP address.
 *
 * If no entry is available in the ARP cache, an incomplete entry is
 * created and ARP requests are generated on increasing time intervals.
 * The calling thread is suspended until a matching ARP response is
 * received or until a number of requests have been generated without
 * receiving a response.
 *
 * \note This function is automatically called on each outgoing
 *       IP packet. Applications typically do not call this function.
 *
 * \param dev  Identifies the device.
 * \param ip   IP address of which the caller asked the MAC address.
 * \param mac  Buffer for the retrieved MAC address.
 *
 * \return 0 if address resolved, -1 otherwise.
 */
int NutArpCacheQuery(NUTDEVICE * dev, CONST u_long ip, u_char * mac)
{
    int rc = -1;
    ARPENTRY *entry;
    IFNET *ifn = dev->dev_icb;
    NETBUF *nb = 0;
    u_char retries = MAX_ARPREQUESTS;
    u_long tmo = MIN_ARPWAIT;

    /* Aging the cache on each query adds some processing to the path 
     * which we want to be as fast as possible. But when calling this 
     * function in NutArpCacheNew only, we will never detect when a 
     * node changes its MAC address. Anyway, the previous solution of 
     * running a timer thread consumed too much RAM.
     */
    ArpCacheAging();

    /*
     * Search a matching entry. If none exists, create a new incomplete 
     * entry and a request packet. If another thread has entered this 
     * routine, an incomplete entry exists and the current thread will 
     * not create a request packet and send out requests.
     */
    if ((entry = ArpCacheLookup(ifn, ip)) == 0) {
        if ((entry = ArpCacheNew(ifn, ip, 0)) == 0) {
            return -1;
        }
        if ((nb = NutArpAllocNetBuf(ARPOP_REQUEST, ip, 0)) == 0) {
            entry->ae_flags |= ATF_REM;
            ArpCacheFlush(ifn);
            return -1;
        }
    }

    /*
     * We enter a loop, which will send ARP requests on increasing 
     * time intervals until our ARP entry gets completed. Give up 
     * after a configured number of retries.
     */
    for (;;) {
        /* If completed, provide the MAC address and exit. */
        if (entry->ae_flags & ATF_COM) {
            //Work around for GCC 3.4.3 bug #18251
            //memcpy(mac, entry->ae_ha, 6);
            //rc = 0;
            rc = 6;
            do {
                rc--;
                mac[rc] = entry->ae_ha[rc];
            } while(rc);
            break;
        }
#ifdef NUTDEBUG
        if (__tcp_trf) {
            fprintf(__tcp_trs, "[%u.ARP-%s %s]",        /* */
                    MAX_ARPREQUESTS - retries + 1,      /* */
                    nb ? "QRY" : "WAIT",        /* */
                    inet_ntoa(ip));
        }
#endif

        /* Give up on too many retries. */
        if (retries-- == 0) {
            break;
        }
        /* Mark buffer released and remove incomplete entry on transmit errors. */
        if (nb && NutArpOutput(dev, nb)) {
            nb = 0;
            /* Even if the transmit failed, we may have received a response in the meantime. */
            if ((entry = ArpCacheLookup(ifn, ip)) != NULL && (entry->ae_flags & ATF_COM) == 0) {
                entry->ae_flags |= ATF_REM;
                ArpCacheFlush(ifn);
            }
            break;
        }
        /* Sleep until woken up by an update of this ARP entry
           or until timeout. Double the timeout on each retry. */
        NutEventWait(&entry->ae_tq, tmo);
        tmo += tmo;

        /* During our sleep, another thread, which created the
           incomplete entry, may have given up and removed the entry. 
           In this case we should also return an error. */
        if ((entry = ArpCacheLookup(ifn, ip)) == 0) {
            break;
        }
    }

    /* Only the thread that created the entry, allocated a request 
       packet. If this thread fails, it should also remove the entry. */
    if (nb) {
        NutNetBufFree(nb);
        /* Play save and check, if the entry still exists. */
        if (rc && entry) {
            entry->ae_flags |= ATF_REM;
            ArpCacheFlush(ifn);
        }
    }
    return rc;
}
Beispiel #15
0
/*
 * Configure-Ack received.
 * Never called in INITIAL or STARTING phase.
 */
static void LcpRxConfAck(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;
    XCPOPT *xcpo;
    uint16_t xcpl;

    /*
     * Check if this is a valid ack.
     */
    if (id == dcb->dcb_reqid && dcb->dcb_acked == 0) {
        dcb->dcb_acked = 1;
        xcpo = nb->nb_ap.vp;
        xcpl = nb->nb_ap.sz;
        while (xcpl >= 2) {
            switch (xcpo->xcpo_type) {
            case LCP_MRU:
                if (htons(xcpo->xcpo_.us) != 1500)
                    dcb->dcb_acked = 0;
                break;
            case LCP_ASYNCMAP:
                //if(ntohl(xcpo->xcpo_.ul) != )
                //    dcb->dcb_acked = 0;
                break;
            case LCP_AUTHTYPE:
                if (htons(xcpo->xcpo_.us) != dcb->dcb_auth)
                    dcb->dcb_acked = 0;
                break;
            case LCP_MAGICNUMBER:
                if (xcpo->xcpo_.ul == dcb->dcb_neg_magic) {
                    dcb->dcb_loc_magic = dcb->dcb_neg_magic;
                } else {
                    dcb->dcb_acked = 0;
                }
                break;
            case LCP_PCOMPRESSION:
                dcb->dcb_acked = 0;
                break;
            case LCP_ACCOMPRESSION:
                dcb->dcb_acked = 0;
                break;
            }
            xcpl -= xcpo->xcpo_len;
            xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
        }
    }

    /*
     * We don't need the NETBUF any more.
     */
    NutNetBufFree(nb);

    /*
     * Ignore invalid acks.
     */
    if (dcb->dcb_acked == 0)
        return;

    switch (dcb->dcb_lcp_state) {
    case PPPS_CLOSED:
    case PPPS_STOPPED:
        /*
         * Go away, we're closed. 
         */
        NutLcpOutput(dev, XCP_TERMACK, id, 0);
        break;

    case PPPS_REQSENT:
        dcb->dcb_lcp_state = PPPS_ACKRCVD;
        dcb->dcb_retries = 0;
        break;

    case PPPS_ACKRCVD:
        LcpTxConfReq(dev, ++dcb->dcb_reqid, 0);
        dcb->dcb_lcp_state = PPPS_REQSENT;
        break;

    case PPPS_ACKSENT:
        /*
         * ACK sent and ACK received.
         */
        dcb->dcb_lcp_state = PPPS_OPENED;

        if (dcb->dcb_auth == PPP_PAP)
            PapTxAuthReq(dev, ++dcb->dcb_reqid);
        else
            IpcpLowerUp(dev);
        break;

    case PPPS_OPENED:
        /* 
         * Go down and restart negotiation.
         */
        IpcpLowerDown(dev);
        LcpTxConfReq(dev, ++dcb->dcb_reqid, 0);
        dcb->dcb_lcp_state = PPPS_REQSENT;
        break;
    }
}
Beispiel #16
0
/*
 * Received Configure-Request.
 */
static void LcpRxConfReq(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;
    int rc = XCP_CONFACK;
    XCPOPT *xcpo;
    uint16_t xcpl;
    XCPOPT *xcpr;
    uint16_t xcps;
    uint16_t len = 0;
    uint16_t sval;
    uint_fast8_t i;

    switch (dcb->dcb_lcp_state) {
    case PPPS_CLOSED:
        /*
         * Go away, we're closed. 
         */
        NutNetBufFree(nb);
        NutLcpOutput(dev, XCP_TERMACK, id, 0);
        return;

    case PPPS_CLOSING:
    case PPPS_STOPPING:
        /*
         * Silently ignore configuration requests while going down.
         */
        NutNetBufFree(nb);
        return;

    case PPPS_OPENED:
        /* 
         * Go down and restart negotiation.
         */
        IpcpLowerDown(dev);
        LcpTxConfReq(dev, ++dcb->dcb_reqid, 0);
        break;

    case PPPS_STOPPED:
        /* 
         * Negotiation started by our peer.
         */
        LcpTxConfReq(dev, ++dcb->dcb_reqid, 0);
        dcb->dcb_lcp_state = PPPS_REQSENT;
        break;
    }

    /*
     * Check if there is anything to reject.
     */
    xcpo = nb->nb_ap.vp;
    xcpl = nb->nb_ap.sz;
    xcpr = nb->nb_ap.vp;
    xcps = 0;
    while (xcpl >= 2) {
        len = xcpo->xcpo_len;
        if (len > xcpl)
            len = xcpl;
        else {
            switch (xcpo->xcpo_type) {
            case LCP_MRU:
                if (xcpo->xcpo_len == 4)
                    len = 0;
                break;
            case LCP_MAGICNUMBER:
            case LCP_ASYNCMAP:
                if (xcpo->xcpo_len == 6)
                    len = 0;
                break;
            case LCP_AUTHTYPE:
                if (xcpo->xcpo_len >= 4)
                    len = 0;
                break;
            case LCP_PCOMPRESSION:
                len = 0;
                break;
            case LCP_ACCOMPRESSION:
                len = 0;
                break;
            }
        }

        if (len) {
            if (xcpr != xcpo) {
                xcpr->xcpo_type = xcpo->xcpo_type;
                xcpr->xcpo_len = len;
                for (i = 0; i < len - 2; i++)
                    /* bug fix by Michel Hendriks. Thanks! */
                    xcpr->xcpo_.uc[i] = xcpo->xcpo_.uc[i];
            }
            xcpr = (XCPOPT *) ((char *) xcpr + len);
            xcps += len;
        }
        xcpl -= xcpo->xcpo_len;
        xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
    }

    if (xcps) {
        nb->nb_ap.sz = xcps;
        rc = XCP_CONFREJ;
    }

    /*
     * Check if there is anything to negotiate.
     */
    else {
        xcpo = nb->nb_ap.vp;
        xcpl = nb->nb_ap.sz;
        xcpr = nb->nb_ap.vp;
        xcps = 0;
        len = 0;
        while (xcpl >= 2) {
            switch (xcpo->xcpo_type) {
            case LCP_MRU:
                if ((sval = htons(xcpo->xcpo_.us)) < MIN_LCPMRU) {
                    len = 4;
                    xcpr->xcpo_.us = ntohs(MIN_LCPMRU);
                } else
                    dcb->dcb_rem_mru = sval;
                break;
            case LCP_ASYNCMAP:
                dcb->dcb_accm = ntohl(xcpo->xcpo_.ul);
                break;
            case LCP_AUTHTYPE:
                if (htons(xcpo->xcpo_.us) != PPP_PAP) {
                    len = 4;
                    xcpr->xcpo_.us = htons(PPP_PAP);
                }
                break;
            case LCP_MAGICNUMBER:
                if (xcpo->xcpo_.ul == dcb->dcb_loc_magic || xcpo->xcpo_.ul == dcb->dcb_neg_magic) {
                    dcb->dcb_rem_magic = new_magic;
                    len = 6;
                    xcpr->xcpo_.ul = dcb->dcb_rem_magic;
                }
                break;
            case LCP_PCOMPRESSION:
                dcb->dcb_compr |= PPP_PFC;
                break;
            case LCP_ACCOMPRESSION:
                dcb->dcb_compr |= PPP_ACFC;
                break;
            }

            if (len) {
                if (xcpr != xcpo) {
                    xcpr->xcpo_type = xcpo->xcpo_type;
                    xcpr->xcpo_len = len;
                }
                xcpr = (XCPOPT *) ((char *) xcpr + len);
                xcps += len;
                len = 0;
            }
            xcpl -= xcpo->xcpo_len;
            xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
        }
        if (xcps) {
            nb->nb_ap.sz = xcps;
            rc = XCP_CONFNAK;
        }
    }

    NutLcpOutput(dev, rc, id, nb);

    if (rc == XCP_CONFACK) {
        if (dcb->dcb_lcp_state == PPPS_ACKRCVD) {
            dcb->dcb_lcp_state = PPPS_OPENED;
            if (dcb->dcb_auth == PPP_PAP)
                PapTxAuthReq(dev, ++dcb->dcb_reqid);
            else
                IpcpLowerUp(dev);
        } else
            dcb->dcb_lcp_state = PPPS_ACKSENT;
        dcb->dcb_lcp_naks = 0;
    } else if (dcb->dcb_lcp_state != PPPS_ACKRCVD)
        dcb->dcb_lcp_state = PPPS_REQSENT;
}
Beispiel #17
0
/*
 * Main application routine.
 *
 * Nut/OS automatically calls this entry after initialization.
 */
int main(void)
{
    uint32_t baud = 115200;
    /* IP of the local network in host byte order. */
    uint32_t net_ip;
    /* Currently scanned IP in host byte order. */
    uint32_t scan_ip;
    /* Currently scanned IP in network byte order. */
    uint32_t dest;
    /* Host portion mask in host byte order. */
    uint32_t host_mask;
    /* Current echo request sequence number. */
    uint16_t seq = 0;

    /*
     * Assign stdout to the DEBUG device.
     */
    NutRegisterDevice(&DEV_CONSOLE, 0, 0);
    freopen(DEV_CONSOLE.dev_name, "w", stdout);
    _ioctl(_fileno(stdout), UART_SETSPEED, &baud);

    /*
     * Print out our version information.
     */
    printf("\n\nNut/OS %s\n", NutVersionString());
    printf("PingNet %s " __DATE__ " " __TIME__ "\n", APP_VERSION);

    /*
     * Configure the network interface. It is assumed, that
     * we got a valid configuration in non-volatile memory.
     *
     * For alternatives see
     * http://www.ethernut.de/nutwiki/Network_Configuration
     */
    printf("Configure %s...", DEV_ETHER_NAME);
    if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
        FatalError("failed");
    }
    if (NutDhcpIfConfig("eth0", 0, 60000)) {
        FatalError("no valid network configuration");
    }
    printf("%s ready\n", inet_ntoa(confnet.cdn_ip_addr));
    /* Some Ethernet device drivers seem to return from initialization
       even if they are not fully up and running. Typically this is no
       problem, because upper layers will do retries. However, in our
       special case we'd lose some nodes during the first scan. So just
       take a short nap to give the driver some more time. */
    NutSleep(2000);

    /*
     * Register an ICMP callback function to inspect all incoming
     * ICMP packets.
     */
    if (NutRegisterIpHandler(IPPROTO_ICMP, IcmpCallback)) {
        FatalError("failed to register ICMP callback");
    }

    /*
     * Determine the host portion mask and the IP of the local
     * network from our IP configuration. Note, that Nut/Net
     * stores all IP address items in network byte order.
     */
    host_mask = ~ntohl(confnet.cdn_ip_mask);
    net_ip = ntohl(confnet.cdn_ip_addr) & ~host_mask;

    /*
     * Allocate a bit field to store the state of all possible
     * nodes of our local network. Limit this to 2^16 (class B)
     * and also reject point to point configurations.
     */
    if (host_mask > 0xFFFF || host_mask < 0x0003) {
        FatalError("Bad network size");
    }
    upnodes = calloc((host_mask + 1) / 8, 1);
    if (upnodes == NULL) {
        FatalError("out of memory");
    }

    /*
     * Scan the whole network endlessly.
     */
    for (;;) {
        int retries = 0;

        seq++;
        scan_ip = net_ip;
        /*
         * Scan node by node.
         */
        for (;;) {
            int got = 0;

            /* If this is not a retry, move to the next possible node
               address. */
            if (retries == 0) {
                scan_ip++;
            }
            /* All nodes processed if we reached the broadcast address. */
            if ((scan_ip & host_mask) == host_mask) {
                break;
            }
            /* Send an echo request to the current IP (network byte order). */
            dest = htonl(scan_ip);
            printf("\r%s ", inet_ntoa(dest));
            if (IcmpSendPing(dest, 1, seq, 32) == 0) {
                /* Wait until our ICMP handler signals new packets. */
                while (got == 0 && NutEventWait(&sign_queue, 100) == 0) {
                    /* Inspect all queued packets. */
                    while (nbuf_queue) {
                        NETBUF *nb;
                        IPHDR *ip = (IPHDR *) nbuf_queue->nb_nw.vp;

                        /* Check if this packet is from the currently scanned
                           node address. We may additionally check ID and
                           sequence number, but actually anything from that
                           interface will be just fine to mark it as up. */
                        got += (ip->ip_src == dest);
                        nb = nbuf_queue;
                        nbuf_queue = nb->nb_next;
                        NutNetBufFree(nb);
                    }
                }
                if (NODE_IS_UP(scan_ip & host_mask)) {
                    /* If the node has been up and is now down, then
                       do a few retries first to be sure that it is
                       really not responding anymore. */
                    if (got == 0) {
                        if (retries < 3) {
                            retries++;
                        } else {
                            retries = 0;
                            NODE_DOWN(scan_ip & host_mask);
                            puts("down");
                        }
                    }
                }
                else if (got) {
                    /* New node detected. */
                    NODE_UP(scan_ip & host_mask);
                    puts("up");
                }
            } else {
                /* Failed to send out the request. */
                puts("ICMP transmit error");
            }
        }
        /* Sleep one minute before scanning the network again. */
        printf("\rSleeping                ");
        NutSleep(60000);
    }
    /* Never reached, but required to suppress compiler warning. */
    return 0;
}
/*!
 * \brief Reject an incoming segment.
 *
 * Send RST in response to an incoming segment, which should
 * be rejected.
 *
 * The function avoids to send out a RST segment in response to 
 * an incoming RST segment.
 *
 * \note This function is mainly used by the TCP state machine.
 *       Applications typically do not call this function.
 *
 * \param nb Network buffer structure of the incoming segment. 
 *           Will be released within this function.
 *
 * \return 0 on success, -1 otherwise.
 */
int NutTcpReject(NETBUF * nb)
{
    u_short csum;
    IPHDR *ih = (IPHDR *) nb->nb_nw.vp;
    TCPHDR *th = (TCPHDR *) nb->nb_tp.vp;

    /*
     * Never send RST in response to RST.
     */
    if (th->th_flags & TH_RST) {
        NutNetBufFree(nb);
        return 0;
    }

    /*
     * Remove any application data and TCP header options.
     */
    nb->nb_ap.sz = 0;
    nb->nb_tp.sz = sizeof(TCPHDR);

    /*
     * Swap ports.
     */
    csum = th->th_sport;
    th->th_sport = th->th_dport;
    th->th_dport = csum;

    /*
     * If the incoming segment has an ACK field, the reset
     * takes its sequence number from the ACK field of the
     * segment, otherwise the reset has sequence number zero
     * and the ACK field is set to the sum of the sequence
     * number and segment length of the incoming segment.
     */
    if (th->th_flags & TH_ACK) {
        th->th_flags = TH_RST;
        th->th_seq = th->th_ack;
        th->th_ack = 0;
    } else {
        if (th->th_flags & TH_SYN)
            th->th_ack = htonl(ntohl(th->th_seq) + 1);
        else
            th->th_ack = th->th_seq;
        th->th_seq = 0;
        th->th_flags = TH_RST | TH_ACK;
    }
    th->th_x2 = 0;
    th->th_off = sizeof(TCPHDR) / 4;
    th->th_win = 0;
    th->th_urp = 0;

    /*
     * Calculate TCP checksum without application data.
     */
    th->th_sum = 0;
    csum =
        NutIpPseudoChkSumPartial(ih->ip_dst, ih->ip_src, IPPROTO_TCP, 
                                 htons(nb->nb_tp.sz));
    th->th_sum = NutIpChkSum(csum, th, nb->nb_tp.sz);

    /*
     * Sent segment back to the source.
     */
#ifdef NUTDEBUG
    if (__tcp_trf)
        NutDumpTcpHeader(__tcp_trs, "REJ", 0, nb);
#endif
    if(NutIpOutput(IPPROTO_TCP, ih->ip_src, nb) == 0)
        NutNetBufFree(nb);
    return 0;
}
Beispiel #19
0
/*!
 * \brief Handle incoming LCP packets.
 *
 * Packets not destined to us or packets with unsupported 
 * address type or item length are silently discarded.
 *
 * \note This routine is called by the Ethernet layer on
 *       incoming ARP packets. Applications typically do
 *       not call this function.
 *
 * \param dev Identifies the device that received the packet.
 * \param nb  Pointer to a network buffer structure containing 
 *            the ARP packet.
 */
void NutLcpInput(NUTDEVICE * dev, NETBUF * nb)
{
    XCPHDR *lcp;
    PPPDCB *dcb = dev->dev_dcb;
    uint16_t len;

    /*
     * Discard packets with illegal lengths.
     */
    if (nb->nb_nw.sz < sizeof(XCPHDR)) {
        NutNetBufFree(nb);
        return;
    }
    lcp = (XCPHDR *) nb->nb_nw.vp;
    if ((len = htons(lcp->xch_len)) < sizeof(XCPHDR) || len > nb->nb_nw.sz) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * Discard all packets while we are in initial or starting state.
     */
    if (dcb->dcb_lcp_state == PPPS_INITIAL || dcb->dcb_lcp_state == PPPS_STARTING) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * Split the LCP packet.
     */
    nb->nb_ap.vp = lcp + 1;
    nb->nb_ap.sz = htons(lcp->xch_len) - sizeof(XCPHDR);

    /*
     * Action depends on code.
     */
    switch (lcp->xch_code) {
    case XCP_CONFREQ:
        LcpRxConfReq(dev, lcp->xch_id, nb);
        break;

    case XCP_CONFACK:
        LcpRxConfAck(dev, lcp->xch_id, nb);
        break;

    case XCP_CONFNAK:
        LcpRxConfNakRej(dev, lcp->xch_id, nb, 0);
        break;

    case XCP_CONFREJ:
        LcpRxConfNakRej(dev, lcp->xch_id, nb, 1);
        break;

    case XCP_TERMREQ:
        LcpRxTermReq(dev, lcp->xch_id, nb);
        break;

    case XCP_TERMACK:
        LcpRxTermAck(dev, lcp->xch_id, nb);
        break;

    case XCP_CODEREJ:
        LcpRxCodeRej(dev, lcp->xch_id, nb);
        break;

    case LCP_ERQ:
        LcpRxEchoReq(dev, lcp->xch_id, nb);
        break;

    case LCP_ERP:
    case LCP_DRQ:
        /* Silently ignore echo responses and discard requests. */
        NutNetBufFree(nb);
        break;

    default:
        /*
         * TODO: Send code reject.
         */
        NutNetBufFree(nb);
        break;
    }
}
/*!
 * \brief Handle incoming ARP packets.
 *
 * Packets not destined to us or packets with unsupported 
 * address type or item length are silently discarded.
 *
 * \note This routine is called by the Ethernet layer on
 *       incoming ARP packets. Applications typically do
 *       not call this function.
 *
 * \param dev Identifies the device that received the packet.
 * \param nb  Pointer to a network buffer structure containing 
 *            the ARP packet.
 */
void NutArpInput(NUTDEVICE * dev, NETBUF * nb)
{
    ETHERARP *ea;
    ARPHDR *ah;
    IFNET *nif;

    ea = (ETHERARP *) nb->nb_nw.vp;
    ah = (ARPHDR *) & ea->ea_hdr;

    /*
     * Silently discard packets with unsupported
     * address types and lengths.
     */
    if (ntohs(ah->ar_hrd) != ARPHRD_ETHER ||
        ntohs(ah->ar_pro) != ETHERTYPE_IP ||
        ah->ar_hln != 6 || ah->ar_pln != 4) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * Silently discard packets for other destinations.
     */
    nif = dev->dev_icb;
    if (ea->arp_tpa != nif->if_local_ip) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * TODO: Silently discard packets with our own 
     * source address.
     */

    /*
     * TODO: Discard packets with broadcast source
     * address.
     */

    /*
     * TODO: Discard packets if source IP address
     * equals our address. We should send a reply.
     */

    /*
     * Add the sender to our arp cache. Note, that we don't do
     * this with replies only, but also with requests on our
     * address. The assumption is that if someone is requesting
     * our address, they are probably intending to talk to us,
     * so it saves time if we cache their address.
     */
    NutArpCacheUpdate(dev, ea->arp_spa, ea->arp_sha);

    /*
     * Reply to ARP requests.
     */
    if (htons(ea->ea_hdr.ar_op) == ARPOP_REQUEST) {
        NETBUF *nbr =
            NutArpAllocNetBuf(ARPOP_REPLY, ea->arp_spa, ea->arp_sha);

        if (nbr) {
            if (!NutArpOutput(dev, nbr))
                NutNetBufFree(nbr);
        }
    }
    NutNetBufFree(nb);
}
Beispiel #21
0
/*!
 * \brief Process incoming IP datagrams.
 * \internal
 *
 * Datagrams addressed to other destinations and datagrams
 * whose version number is not 4 are silently discarded.
 *
 * This routine is called by the Ethernet layer on incoming IP datagrams.
 *
 * \param dev Identifies the device that received this datagram.
 * \param nb  The network buffer received.
 */
void NutIpInput(NUTDEVICE * dev, NETBUF * nb)
{
    IPHDR *ip;
    uint_fast8_t hdrlen;
    uint32_t dst;
    uint_fast8_t bcast = 0;
    IFNET *nif;

    ip = nb->nb_nw.vp;

    /*
     * Silently discard datagrams of different IP version as well as
     * fragmented or filtered datagrams.
     */
    if (ip->ip_v != IPVERSION ||        /* Version check. */
        (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) != 0 ||      /* Fragmentation. */
        (NutIpFilter && NutIpFilter(ip->ip_src))) {     /* Filter. */
        NutNetBufFree(nb);
        return;
    }

    /*
    ** IP header length is given in 32-bit fields. Calculate the size in
    ** bytes and make sure that the header we know will fit in. Check
    ** further, that the header length is not larger than the bytes we
    ** received.
    */
    hdrlen = ip->ip_hl * 4;
    if (hdrlen < sizeof(IPHDR) || hdrlen > nb->nb_nw.sz) {
        NutNetBufFree(nb);
        return;
    }

#if NUT_IP_INCHKSUM
    /* Optional checksum calculation on incoming datagrams. */
#endif

    dst = ip->ip_dst;
    nif = dev->dev_icb;

    /*
     * Check for limited broadcast.
     */
    if (dst == INADDR_BROADCAST) {
        bcast = 1;
    }

    /*
     * Check for multicast.
     */
    else if (IN_MULTICAST(dst)) {
        MCASTENTRY *mca;

        for (mca = nif->if_mcast; mca; mca = mca->mca_next) {
            if (dst == mca->mca_ip) {
                break;
            }
        }
        if (mca == NULL) {
            NutNetBufFree(nb);
            return;
        }
        bcast = 2;
    }

    /*
     * Check device's local IP address.
     */
    else if (nif->if_local_ip != 0) {
        /*
         * Check for unicast.
         */
        if (dst == nif->if_local_ip) {
            nb->nb_flags |= NBAF_UNICAST;
        }

        /*
         * Check for net-directed broadcast.
         */
        else if (~nif->if_mask && (dst & ~nif->if_mask) == ~nif->if_mask) {
            bcast = 1;
        }

        /*
         * Not for us, discard silently.
         */
        else {
            NutIpForward(nb);
            NutNetBufFree(nb);
            return;
        }
    }

    /*
     * Calculate IP data length.
     */
    nb->nb_tp.sz = htons(ip->ip_len);
    if (nb->nb_tp.sz < hdrlen || nb->nb_tp.sz > nb->nb_nw.sz) {
        NutNetBufFree(nb);
        return;
    }
    nb->nb_nw.sz = hdrlen;
    nb->nb_tp.sz -= hdrlen;
    if (nb->nb_tp.sz) {
        nb->nb_tp.vp = ((char *) ip) + hdrlen;
    }

    /*
     * Route valid datagram to the related handler.
     */
    if (ip_demux == NULL || (*ip_demux) (dev, nb)) {
        switch (ip->ip_p) {
        case IPPROTO_ICMP:
            NutIcmpInput(dev, nb);
            break;
        case IPPROTO_IGMP:
            NutIgmpInput(dev, nb);
            break;
        default:
            /* Unknown protocol, send ICMP destination (protocol)
            * unreachable message.
            */
            if (bcast || !NutIcmpResponse(ICMP_UNREACH, ICMP_UNREACH_PROTOCOL, 0, nb))
                NutNetBufFree(nb);
            break;
        }
    }
}
Beispiel #22
0
/*
 * Configure-Ack received.
 */
static void IpcpRxConfAck(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;
    XCPOPT *xcpo;
    uint16_t xcpl;

    /*
     * Ignore, if we are not expecting this id.
     */
    if (id != dcb->dcb_reqid || dcb->dcb_acked) {
        return;
    }

    switch (dcb->dcb_ipcp_state) {
    case PPPS_CLOSED:
    case PPPS_STOPPED:
        /*
         * Go away, we're closed.
         */
        NutNetBufFree(nb);
        NutIpcpOutput(dev, XCP_TERMACK, id, 0);
        return;

    case PPPS_REQSENT:
        dcb->dcb_ipcp_state = PPPS_ACKRCVD;
        dcb->dcb_retries = 0;
        break;

    case PPPS_ACKRCVD:
        /* Huh? an extra valid Ack? oh well... */
        IpcpTxConfReq(dev, ++dcb->dcb_reqid);
        dcb->dcb_ipcp_state = PPPS_REQSENT;
        break;

    case PPPS_ACKSENT:
        dcb->dcb_ipcp_state = PPPS_OPENED;
        dcb->dcb_retries = 0;
        NutEventPost(&dcb->dcb_state_chg);
        break;

    case PPPS_OPENED:
        /* Go down and restart negotiation */
        IpcpLowerDown(dev);
        IpcpTxConfReq(dev, ++dcb->dcb_reqid);
        dcb->dcb_ipcp_state = PPPS_REQSENT;
        break;
    }

    xcpo = nb->nb_ap.vp;
    xcpl = nb->nb_ap.sz;
    while (xcpl >= 2) {
        switch (xcpo->xcpo_type) {
        case IPCP_ADDR:
            if (xcpo->xcpo_.ul)
                dcb->dcb_local_ip = xcpo->xcpo_.ul;
            break;
        case IPCP_COMPRESSTYPE:
            break;
        case IPCP_MS_DNS1:
            if (xcpo->xcpo_.ul)
                dcb->dcb_ip_dns1 = xcpo->xcpo_.ul;
            break;
        case IPCP_MS_DNS2:
            /* Fixed secondary DNS bug, thanks to Tarmo Fimberg
               and Jelle Martijn Kok. */
            if (xcpo->xcpo_.ul)
                dcb->dcb_ip_dns2 = xcpo->xcpo_.ul;
            break;
        }
        xcpl -= xcpo->xcpo_len;
        xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
    }

    dcb->dcb_acked = 1;
    NutNetBufFree(nb);
}
/*!
 * \brief Allocate or re-allocate a network buffer part.
 *
 * \param nb   Points to an existing network buffer structure or NULL, 
 *             if a new structure should be created. An existing buffer
 *             must not be used any further if this function returns
 *             a null pointer.
 * \param type Part of the buffer to be allocated. This can be any of
 *             the following:
 *             - NBAF_DATALINK
 *             - NBAF_NETWORK
 *             - NBAF_TRANSPORT
 *             - NBAF_APPLICATION
 * \param size Size of the part to be allocated.
 *
 * \return Pointer to the allocated network buffer structure. A null
 *         pointer is returned if not enough memory is available and
 *         the whole structure is released.
 */
NETBUF *NutNetBufAlloc(NETBUF * nb, u_char type, u_short size)
{
    if (nb == 0) {
        nb = NutHeapAllocClear(sizeof(NETBUF));
    }

    if (nb && type) {
        if (size) {
            if (type & NBAF_DATALINK) {
                if (nb->nb_flags & NBAF_DATALINK) {
                    if (nb->nb_dl.sz < size) {
                        NutNetBufFreeData(&nb->nb_dl);
                        if (NutNetBufAllocData(&nb->nb_dl, size)) {
                            NutNetBufFree(nb);
                            return 0;
                        }
                    } else
                        nb->nb_dl.sz = size;
                } else if (NutNetBufAllocData(&nb->nb_dl, size)) {
                    NutNetBufFree(nb);
                    return 0;
                }
            }

            if (type & NBAF_NETWORK) {
                if (nb->nb_flags & NBAF_NETWORK) {
                    if (nb->nb_nw.sz < size) {
                        NutNetBufFreeData(&nb->nb_nw);
                        if (NutNetBufAllocData(&nb->nb_nw, size)) {
                            NutNetBufFree(nb);
                            return 0;
                        }
                    } else
                        nb->nb_nw.sz = size;
                } else if (NutNetBufAllocData(&nb->nb_nw, size)) {
                    NutNetBufFree(nb);
                    return 0;
                }
            }

            if (type & NBAF_TRANSPORT) {
                if (nb->nb_flags & NBAF_TRANSPORT) {
                    if (nb->nb_tp.sz < size) {
                        NutNetBufFreeData(&nb->nb_tp);
                        if (NutNetBufAllocData(&nb->nb_tp, size)) {
                            NutNetBufFree(nb);
                            return 0;
                        }
                    } else
                        nb->nb_tp.sz = size;
                } else if (NutNetBufAllocData(&nb->nb_tp, size)) {
                    NutNetBufFree(nb);
                    return 0;
                }
            }

            if (type & NBAF_APPLICATION) {
                if (nb->nb_flags & NBAF_APPLICATION) {
                    if (nb->nb_ap.sz < size) {
                        NutNetBufFreeData(&nb->nb_ap);
                        if (NutNetBufAllocData(&nb->nb_ap, size)) {
                            NutNetBufFree(nb);
                            return 0;
                        }
                    } else
                        nb->nb_ap.sz = size;
                } else if (NutNetBufAllocData(&nb->nb_ap, size)) {
                    NutNetBufFree(nb);
                    return 0;
                }
            }
            nb->nb_flags |= type;
        } else {
            type &= nb->nb_flags;
            if (type & NBAF_DATALINK)
                NutNetBufFreeData(&nb->nb_dl);
            if (type & NBAF_NETWORK)
                NutNetBufFreeData(&nb->nb_nw);
            if (type & NBAF_TRANSPORT)
                NutNetBufFreeData(&nb->nb_tp);
            if (type & NBAF_APPLICATION)
                NutNetBufFreeData(&nb->nb_ap);
            nb->nb_flags &= ~type;
        }
    }
    return nb;
}
Beispiel #24
0
/*!
 * \brief Handle incoming IPCP packets.
 *
 * Packets not destined to us or packets with unsupported
 * address type or item length are silently discarded.
 *
 * \note This routine is called by the Ethernet layer on
 *       incoming ARP packets. Applications typically do
 *       not call this function.
 *
 * \param dev Identifies the device that received the packet.
 * \param nb  Pointer to a network buffer structure containing
 *            the ARP packet.
 */
void NutIpcpInput(NUTDEVICE * dev, NETBUF * nb)
{
    XCPHDR *xch;
    PPPDCB *dcb = dev->dev_dcb;
    uint16_t len;

    /*
     * Discard packets with illegal lengths.
     */
    if (nb->nb_nw.sz < sizeof(XCPHDR)) {
        NutNetBufFree(nb);
        return;
    }
    xch = (XCPHDR *) nb->nb_nw.vp;
    if ((len = htons(xch->xch_len)) < sizeof(XCPHDR) || len > nb->nb_nw.sz) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * Discard all packets while we are in initial or starting state.
     */
    if (dcb->dcb_ipcp_state == PPPS_INITIAL || dcb->dcb_ipcp_state == PPPS_STARTING) {
        NutNetBufFree(nb);
        return;
    }

    /*
     * Split the IPCP packet.
     */
    nb->nb_ap.vp = xch + 1;
    nb->nb_ap.sz = htons(xch->xch_len) - sizeof(XCPHDR);

    /*
     * Action depends on code.
     */
    switch (xch->xch_code) {
    case XCP_CONFREQ:
        IpcpRxConfReq(dev, xch->xch_id, nb);
        break;

    case XCP_CONFACK:
        IpcpRxConfAck(dev, xch->xch_id, nb);
        break;

    case XCP_CONFNAK:
        IpcpRxConfNakRej(dev, xch->xch_id, nb, 0);
        break;

    case XCP_CONFREJ:
        IpcpRxConfNakRej(dev, xch->xch_id, nb, 1);
        break;

    case XCP_TERMREQ:
        IpcpRxTermReq(dev, xch->xch_id, nb);
        break;

    case XCP_TERMACK:
        IpcpRxTermAck(dev, xch->xch_id, nb);
        break;

    case XCP_CODEREJ:
        IpcpRxCodeRej(dev, xch->xch_id, nb);
        break;

    default:
        /*
         * TODO: Send code reject.
         */
        NutNetBufFree(nb);
        break;
    }
}
Beispiel #25
0
/*
 * Received Configure-Request.
 */
static void IpcpRxConfReq(NUTDEVICE * dev, uint8_t id, NETBUF * nb)
{
    PPPDCB *dcb = dev->dev_dcb;
    int rc = XCP_CONFACK;
    XCPOPT *xcpo;
    uint16_t xcpl;
    XCPOPT *xcpr;
    uint16_t xcps;
    uint16_t len = 0;
    uint_fast8_t i;
    uint32_t ip;

    switch (dcb->dcb_ipcp_state) {
    case PPPS_CLOSED:
        /*
         * Go away, we're closed.
         */
        NutNetBufFree(nb);
        NutIpcpOutput(dev, XCP_TERMACK, id, 0);
        return;

    case PPPS_CLOSING:
    case PPPS_STOPPING:
        /*
         * Ignore if we are going down.
         */
        NutNetBufFree(nb);
        return;

    case PPPS_OPENED:
        /*
         * Go down and restart negotiation.
         */
        IpcpLowerDown(dev);
        IpcpTxConfReq(dev, ++dcb->dcb_reqid);
        break;

    case PPPS_STOPPED:
        /*
         * Negotiation started by our peer.
         */
        IpcpTxConfReq(dev, ++dcb->dcb_reqid);
        dcb->dcb_ipcp_state = PPPS_REQSENT;
        break;
    }

    /*
     * Check if there is anything to reject.
     */
    xcpo = nb->nb_ap.vp;
    xcpl = nb->nb_ap.sz;
    xcpr = nb->nb_ap.vp;
    xcps = 0;
    while (xcpl >= 2) {
        len = xcpo->xcpo_len;
        if (len > xcpl)
            len = xcpl;
        else {
            switch (xcpo->xcpo_type) {
            case IPCP_MS_DNS1:
                if (xcpo->xcpo_.ul == 0 && dcb->dcb_ip_dns1 == 0) {
                    break;
                }
            case IPCP_MS_DNS2:
                if (xcpo->xcpo_.ul == 0 && dcb->dcb_ip_dns2 == 0) {
                    break;
                }
            case IPCP_ADDR:
                if (xcpo->xcpo_len == 6)
                    len = 0;
                break;
            }
        }

        if (len) {
            if (xcpr != xcpo) {
                xcpr->xcpo_type = xcpo->xcpo_type;
                xcpr->xcpo_len = len;
                for (i = 0; i < len - 2; i++)
                    xcpr->xcpo_.uc[i] = xcpo->xcpo_.uc[i];
            }
            xcpr = (XCPOPT *) ((char *) xcpr + len);
            xcps += len;
        }
        xcpl -= xcpo->xcpo_len;
        xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
    }

    if (xcps) {
        nb->nb_ap.sz = xcps;
        rc = XCP_CONFREJ;
    }

    /*
     * Check if there is anything to negotiate.
     */
    else {
        xcpo = nb->nb_ap.vp;
        xcpl = nb->nb_ap.sz;
        xcpr = nb->nb_ap.vp;
        xcps = 0;
        len = 0;
        while (xcpl >= 2) {
            ip = xcpo->xcpo_.ul;
            switch (xcpo->xcpo_type) {
            case IPCP_ADDR:
                len = IpcpValidateIpReq(&dcb->dcb_remote_ip, &ip);
                break;
            case IPCP_MS_DNS1:
                len = IpcpValidateIpReq(&dcb->dcb_ip_dns1, &ip);
                break;
            case IPCP_MS_DNS2:
                len = IpcpValidateIpReq(&dcb->dcb_ip_dns2, &ip);
                break;
            }

            if (len) {
                if (xcpr != xcpo) {
                    xcpr->xcpo_type = xcpo->xcpo_type;
                    xcpr->xcpo_len = len;
                }
                xcpr->xcpo_.ul = ip;
                xcpr = (XCPOPT *) ((char *) xcpr + len);
                xcps += len;
                len = 0;
            }
            xcpl -= xcpo->xcpo_len;
            xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
        }
        if (xcps) {
            nb->nb_ap.sz = xcps;
            rc = XCP_CONFNAK;
        }
    }

    NutIpcpOutput(dev, rc, id, nb);

    if (rc == XCP_CONFACK) {
        if (dcb->dcb_ipcp_state == PPPS_ACKRCVD) {
            dcb->dcb_ipcp_state = PPPS_OPENED;
            NutEventPost(&dcb->dcb_state_chg);
        } else
            dcb->dcb_ipcp_state = PPPS_ACKSENT;
        dcb->dcb_ipcp_naks = 0;
    } else if (dcb->dcb_ipcp_state != PPPS_ACKRCVD)
        dcb->dcb_ipcp_state = PPPS_REQSENT;
}
Beispiel #26
0
/*
 * Configure-Nak or Configure-Reject received.
 */
static void IpcpRxConfNakRej(NUTDEVICE * dev, uint8_t id, NETBUF * nb, uint8_t rejected)
{
    PPPDCB *dcb = dev->dev_dcb;
    XCPOPT *xcpo;
    uint16_t xcpl;

    /*
     * Ignore, if we are not expecting this id.
     */
    if (id != dcb->dcb_reqid || dcb->dcb_acked) {
        NutNetBufFree(nb);
        return;
    }

    switch (dcb->dcb_ipcp_state) {
    case PPPS_CLOSED:
    case PPPS_STOPPED:
        /*
         * Go away, we're closed.
         */
        NutNetBufFree(nb);
        NutIpcpOutput(dev, XCP_TERMACK, id, 0);
        return;

    case PPPS_REQSENT:
    case PPPS_ACKSENT:
    case PPPS_ACKRCVD:
    case PPPS_OPENED:
        break;

    default:
        NutNetBufFree(nb);
        return;
    }

    dcb->dcb_acked = 1;

    xcpo = nb->nb_ap.vp;
    xcpl = nb->nb_ap.sz;
    while (xcpl >= 2) {
        switch (xcpo->xcpo_type) {
        case IPCP_ADDR:
            if (xcpo->xcpo_.ul)
                dcb->dcb_local_ip = xcpo->xcpo_.ul;
            break;
        case IPCP_COMPRESSTYPE:
            break;
        case IPCP_MS_DNS1:
            if (rejected)
                dcb->dcb_rejects |= REJ_IPCP_DNS1;
            else if (xcpo->xcpo_.ul)
                dcb->dcb_ip_dns1 = xcpo->xcpo_.ul;
            break;
        case IPCP_MS_DNS2:
            if (rejected)
                dcb->dcb_rejects |= REJ_IPCP_DNS2;
            else if (xcpo->xcpo_.ul)
                dcb->dcb_ip_dns2 = xcpo->xcpo_.ul;
            break;
        }
        xcpl -= xcpo->xcpo_len;
        xcpo = (XCPOPT *) ((char *) xcpo + xcpo->xcpo_len);
    }
    NutNetBufFree(nb);

    switch (dcb->dcb_ipcp_state) {

    case PPPS_REQSENT:
    case PPPS_ACKSENT:
        /* They didn't agree to what we wanted - try another request */
        IpcpTxConfReq(dev, ++dcb->dcb_reqid);
        break;

    case PPPS_ACKRCVD:
        /* Got a Nak/reject when we had already had an Ack?? oh well... */
        IpcpTxConfReq(dev, ++dcb->dcb_reqid);
        dcb->dcb_ipcp_state = PPPS_REQSENT;
        break;

    case PPPS_OPENED:
        /* Go down and restart negotiation */
        IpcpLowerDown(dev);
        IpcpTxConfReq(dev, ++dcb->dcb_reqid);
        dcb->dcb_ipcp_state = PPPS_REQSENT;
        break;
    }
}
/*!
 * \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;
}