Example #1
0
static void udpStat(struct udp *udpptr)
{
    device *pdev;
    char strA[20];

    if (NULL == udpptr)
    {
        return;
    }

    /* Skip interface if not allocated */
    if (udpptr->state != UDP_OPEN)
    {
        return;
    }

    /* Setup pointer to underlying device */
    pdev = udpptr->dev;
    printf("%-10s ", pdev->name);

    /* Connection details */
    netaddrsprintf(strA, &udpptr->localip);
    printf("Local  Port: %-5d IP: %-15s\n", udpptr->localpt, strA);
    printf("           ");
    netaddrsprintf(strA, &udpptr->remoteip);
    printf("Remote Port: %-5d IP: %-15s\n", udpptr->remotept, strA);
}
Example #2
0
static void netStat(struct netif *netptr)
{
    device *pdev;
    char strA[20];
    char strB[20];

    /* Skip interface if not allocated */
    if ((NULL == netptr) || (netptr->state != NET_ALLOC))
    {
        return;
    }

    /* Setup pointer to underlying device */
    pdev = (device *)&devtab[netptr->dev];
    printf("%s:\n", pdev->name);
    netaddrsprintf(strA, &netptr->hwaddr);
    printf("\t");
    printf("HW Addr: %s\n", strA);
    netaddrsprintf(strA, &netptr->ip);
    netaddrsprintf(strB, &netptr->mask);
    printf("\t");
    printf("IP Addr: %-15s   Mask: %-15s\n", strA, strB);
    netaddrsprintf(strA, &netptr->gateway);
    netaddrsprintf(strB, &netptr->ipbrc);
    printf("\t");
    printf("Gateway: %-15s   Bcast IP: %-15s\n", strA, strB);
    printf("\t");
    printf("MTU: %-19d   Link Hdr Len: %d\n", netptr->mtu,
           netptr->linkhdrlen);
    printf("\t");
    printf("Num Rcv: %-15d   Num Proc: %d\n", netptr->nin, netptr->nproc);

    return;
}
Example #3
0
/**
 * Shell command (arp).
 * @param nargs  number of arguments in args array
 * @param args   array of arguments
 * @return OK for success, SYSERR for syntax error
 */
shellcmd xsh_arp(int nargs, char *args[])
{

    int i;
    char c[32];
    device *pdev;
    struct netif *netptr;

    /* Output help, if '--help' argument was supplied */
    if (nargs == 2 && strncmp(args[1], "--help", 7) == 0)
    {
        printf("Usage: %s\n\n", args[0]);
        printf("Description:\n");
        printf("\tDisplays ARP Information\n");
        printf("Options:\n");
        printf("\t--help\tdisplay this help and exit\n");
        return OK;
    }

    /* Check for correct number of arguments */
    if (nargs > 1)
    {
        fprintf(stderr, "%s: too many arguments\n", args[0]);
        fprintf(stderr, "Try '%s --help' for more information\n",
                args[0]);
        return SYSERR;
    }

    printf
        ("Address                 HWaddress               Interface\r\n");
    for (i = 0; i < ARP_NENTRY; i++)
    {
        if (arptab[i].state & ARP_USED)
        {
            netaddrsprintf(c, &arptab[i].praddr);
            printf("%-24s", c);

            netaddrsprintf(c, &arptab[i].hwaddr);
            printf("%-24s", c);

            netptr = arptab[i].nif;
            pdev = (device *)&devtab[netptr->dev];
            printf("%s\r\n", pdev->name);
        }
    }

    return OK;
}
Example #4
0
/**
 * Print contents of Ethernet packet
 * @param ether Ethernet packet
 * @return OK if print successful, SYSERR if error occurs
 */
int snoopPrintEthernet(struct etherPkt *ether, char verbose)
{
    char output[40];
    struct netaddr hwaddr;

    /* Error check pointer */
    if (NULL == ether)
    {
        return SYSERR;
    }

    if (verbose >= SNOOP_VERBOSE_TWO)
    {
        /* Print ethernet header */
        printf(" ----- Ethernet Header -----\n", "");
        hwaddr.type = NETADDR_ETHERNET;
        hwaddr.len = ETH_ADDR_LEN;
        memcpy(hwaddr.addr, ether->dst, hwaddr.len);
        netaddrsprintf(output, &hwaddr);
        printf("  Dst: %-25s ", output);
        memcpy(hwaddr.addr, ether->src, hwaddr.len);
        netaddrsprintf(output, &hwaddr);
        printf("Src: %-25s ", output);
        switch (net2hs(ether->type))
        {
        case ETHER_TYPE_IPv4:
            sprintf(output, "IP");
            break;
        case ETHER_TYPE_ARP:
            sprintf(output, "ARP");
            break;
        default:
            sprintf(output, "0x%04X", net2hs(ether->type));
            break;
        }
        printf("Type: %-5s\n", output);
    }

    return OK;
}
Example #5
0
/**
 * Print out a echo reply packet.
 */
static void echoPrintPkt(struct packet *pkt, ulong elapsed)
{
    struct icmpPkt *icmp = NULL;
    struct icmpEcho *echo = NULL;
    struct ipv4Pkt *ip = NULL;
    struct netaddr src;
    char str[50];

    ip = (struct ipv4Pkt *)pkt->nethdr;
    icmp = (struct icmpPkt *)pkt->curr;
    echo = (struct icmpEcho *)icmp->data;

    src.type = NETADDR_IPv4;
    src.len = IPv4_ADDR_LEN;
    memcpy(src.addr, ip->src, src.len);

    netaddrsprintf(str, &src);
    printf("%d bytes from %s: ", net2hs(ip->len), str);
    printf("icmp_seq=%d ttl=%d ", net2hs(echo->seq), ip->ttl);
    printf("time=%d.%03d ms\n", elapsed / 1000, elapsed % 1000);
}
Example #6
0
/**
 * Transmission Control Protocol status command.
 * @param tcbptr pointer to transmission control block
 */
void tcpStat(struct tcb *tcbptr)
{
    device *pdev;
    struct tcb copy;
    char strA[20];
    char strB[20];

    if (NULL == tcbptr)
    {
        return;
    }

    wait(tcbptr->mutex);
    /* Take atomic snapshot of control block */
    memcpy(&copy, tcbptr, sizeof(struct tcb));
    signal(tcbptr->mutex);

    /* Skip interface if not allocated */
    if (copy.devstate != TCP_ALLOC)
    {
        printf("BLOCK%-3d   Inactive\n", tcbptr - tcptab);
        return;
    }

    /* Setup pointer to underlying device */
    pdev = (device *)&devtab[copy.dev];
    printf("%-10s ", pdev->name);

    switch (copy.state)
    {
    case TCP_CLOSED:
        sprintf(strA, "Closed");
        break;
    case TCP_LISTEN:
        sprintf(strA, "Listen");
        break;
    case TCP_SYNSENT:
        sprintf(strA, "Syn Sent");
        break;
    case TCP_SYNRECV:
        sprintf(strA, "Syn Recv'd");
        break;
    case TCP_ESTAB:
        sprintf(strA, "Established");
        break;
    case TCP_FINWT1:
        sprintf(strA, "Fin Wait 1");
        break;
    case TCP_FINWT2:
        sprintf(strA, "Fin Wait 2");
        break;
    case TCP_CLOSEWT:
        sprintf(strA, "Close Wait");
        break;
    case TCP_CLOSING:
        sprintf(strA, "Closing");
        break;
    case TCP_LASTACK:
        sprintf(strA, "Last Ack");
        break;
    case TCP_TIMEWT:
        sprintf(strA, "Time Wait");
        break;
    default:
        sprintf(strA, "Unknown");
        break;
    }
    switch (copy.opentype)
    {
    case TCP_PASSIVE:
        sprintf(strB, "Passive");
        break;
    case TCP_ACTIVE:
        sprintf(strB, "Active");
        break;
    default:
        sprintf(strB, "Unknown");
        break;
    }
    printf("State: %-11s    Open Type: %-7s\n", strA, strB);
    printf("           ");

    /* Connection details */
    netaddrsprintf(strA, &copy.localip);
    printf("Local  Port: %-5d    IP: %-15s\n", copy.localpt, strA);
    printf("           ");
    netaddrsprintf(strA, &copy.remoteip);
    printf("Remote Port: %-5d    IP: %-15s\n", copy.remotept, strA);

    /* Sequence numbers */
    printf("           ");
    printf("Rcv Nxt: %-10u   Wnd: %-10u\n", copy.rcvnxt,
           tcpSeqdiff(copy.rcvwnd, copy.rcvnxt));
    printf("           ");
    printf("Snd Una: %-10u   Nxt: %-10u   Wnd: %-10u\n",
           copy.snduna, copy.sndnxt, copy.sndwnd);

    /* Buffers */
    printf("           ");
    printf("In  Start: %-10u Count: %-10u Read %-10u\n",
           copy.istart, copy.icount, copy.ibytes);
    printf("           ");
    printf("Out Start: %-10u Count: %-10u Read %-10u\n",
           copy.ostart, copy.ocount, copy.obytes);
    printf("\n");

    return;
}
Example #7
0
/**
 * Receive a UDP packet and place it in the UDP device's input buffer
 * @param pkt Incoming UDP packet
 * @param src Source address
 * @param dst Destination address
 * @return OK if UDP packet is received properly, otherwise SYSERR
 */
syscall udpRecv(struct packet *pkt, struct netaddr *src,
                struct netaddr *dst)
{
    struct udpPkt *udppkt;
    struct udp *udpptr;
    struct udpPkt *tpkt;
#ifdef TRACE_UDP
    char strA[20];
    char strB[20];
#endif                          /* TRACE_UDP */

    irqmask im;

    /* Point to the start of the UDP header */
    udppkt = (struct udpPkt *)pkt->curr;

    if (NULL == udppkt)
    {
        UDP_TRACE("Invalid UDP packet.");
        netFreebuf(pkt);
        return SYSERR;
    }

    /* Calculate checksum */
    if (0 != udpChksum(pkt, net2hs(udppkt->len), src, dst))
    {
        UDP_TRACE("Invalid UDP checksum.");
        netFreebuf(pkt);
        return SYSERR;
    }

    /* Convert UDP header fields to host order */
    udppkt->srcPort = net2hs(udppkt->srcPort);
    udppkt->dstPort = net2hs(udppkt->dstPort);
    udppkt->len = net2hs(udppkt->len);

    im = disable();

    /* Locate the UDP socket (device) for the UDP packet */
    udpptr = udpDemux(udppkt->dstPort, udppkt->srcPort, dst, src);

    if (NULL == udpptr)
    {
#ifdef TRACE_UDP
        UDP_TRACE("No UDP socket found for this UDP packet.");
        netaddrsprintf(strA, src);
        netaddrsprintf(strB, dst);
        UDP_TRACE("Source: %s:%d, Destination: %s:%d", strA,
                  udppkt->srcPort, strB, udppkt->dstPort);
#endif                          /* TRACE_UDP */
        restore(im);
        /* Send ICMP port unreachable message */
        icmpDestUnreach(pkt, ICMP_PORT_UNR);
        netFreebuf(pkt);
        return SYSERR;
    }
    if (udpptr->icount >= UDP_MAX_PKTS)
    {
        UDP_TRACE("UDP buffer is full. Dropping UDP packet.");
        restore(im);
        netFreebuf(pkt);
        return SYSERR;
    }

    /* Check "bind first" flag and update connection if set,
     * and clear the flag */
    if (UDP_FLAG_BINDFIRST & udpptr->flags)
    {
        udpptr->remotept = udppkt->srcPort;
        netaddrcpy(&(udpptr->localip), dst);
        netaddrcpy(&(udpptr->remoteip), src);
        udpptr->flags &= ~UDP_FLAG_BINDFIRST;
    }

    /* Get some buffer space to store the packet */
    tpkt = udpGetbuf(udpptr);

    if (SYSERR == (int)tpkt)
    {
        UDP_TRACE("Unable to get UDP buffer from pool. Dropping packet.");
        netFreebuf(pkt);
        return SYSERR;
    }

    /* Copy the data of the packet into the input buffer at the current 
     * position */
    memcpy(tpkt, udppkt, udppkt->len);

    /* Store the temporary UDP packet in a FIFO buffer */
    udpptr->in[(udpptr->istart + udpptr->icount) % UDP_MAX_PKTS] = tpkt;
    udpptr->icount++;

    restore(im);

    signal(udpptr->isem);

    netFreebuf(pkt);

    return OK;
}
Example #8
0
/**
 * @ingroup shell
 *
 * Shell command (ping).
 * @param nargs  number of arguments in args array
 * @param args   array of arguments
 * @return OK for success, SYSERR for syntax error
 */
shellcmd xsh_ping(int nargs, char *args[])
{
    int i = 0;
    int interval = 1000, count = 10, recv = 0, echoq = 0;
    ulong rtt = 0, min = 0, max = 0, total = 0;
    ulong startsec = 0, startms = 0;
    struct netaddr target;
    struct packet *pkt = NULL;
    char str[50];

    /* Output help, if '--help' argument was supplied */
    if (nargs == 2 && strncmp(args[1], "--help", 7) == 0)
    {
        printf("Usage: ping <IP>\n\n");
        printf("Description:\n");
        printf("\tSend ICMP echo requests to network hosts\n");
        printf("Options:\n");
        printf("\t<IP>\t\tIP address\n");
        printf("\t-c count\tstop after sending count packets\n");
        printf
            ("\t-i interval\tsleep interval milliseconds between pings\n");
        printf("\t--help\t\tdisplay this help and exit\n");
        return OK;
    }

    /* Check for correct number of arguments */
    if (nargs < 2)
    {
        fprintf(stderr, "ping: too few arguments\n");
        fprintf(stderr, "Try 'ping --help' for more information\n");
        return SHELL_ERROR;
    }

    i = 1;
    while (i < nargs)
    {
        if (0 == strncmp(args[i], "-c", 3))
        {
            i++;
            if (i < nargs)
            {
                count = atoi(args[i]);
            }
            else
            {
                fprintf(stderr, "ping: -c requires integer argument\n");
                return SHELL_ERROR;
            }
        }
        else if (0 == strncmp(args[i], "-i", 3))
        {
            i++;
            if (i < nargs)
            {
                interval = atoi(args[i]);
            }
            else
            {
                fprintf(stderr, "ping: -i requires integer argument\n");
                return SHELL_ERROR;
            }
        }
        else if (SYSERR == dot2ipv4(args[i], &target))
        {
            fprintf(stderr, "ping: %s is not a valid IPv4 address.\n",
                    args[i]);
            return SHELL_ERROR;
        }
        i++;
    }

    netaddrsprintf(str, &target);
    if (0 == strncmp(str, "ERROR", 6))
    {
        fprintf(stderr, "ping: destination IP address required.\n");
        return SHELL_ERROR;
    }
    printf("PING %s\n", str);

    echoq = echoQueueAlloc();
    if (SYSERR == echoq)
    {
        printf("...No free echo queues!\n");
        return SHELL_ERROR;
    }

    startsec = clktime;
    startms = clkticks;

    for (i = 0; i < count; i++)
    {
        // Send ping packet
        if (SYSERR == icmpEchoRequest(&target, gettid(), i))
        {
            printf("...Failed to reach %s\n", str);
            return SHELL_ERROR;
        }

        sleep(interval);
        if (NOMSG != recvclr())
        {
            // pick reply packets off of the queue
            pkt = echoQueueGet(echoq);
            while ((NULL != (ulong)pkt) && (SYSERR != (ulong)pkt))
            {
                rtt = echoTripTime(pkt);
                total += rtt;
                if ((rtt < min) || (0 == min))
                {
                    min = rtt;
                }
                if (rtt > max)
                {
                    max = rtt;
                }

                echoPrintPkt(pkt, rtt);
                netFreebuf(pkt);
                recv++;
                pkt = echoQueueGet(echoq);
            }
        }
    }
    echoQueueDealloc(echoq);

    netaddrsprintf(str, &target);
    printf("--- %s ping statistics ---\n", str);
    printf("%d packets transmitted, %d received,", count, recv);
    printf(" %d%% packet loss,", (count - recv) * 100 / count);
    printf(" time %dms\n", (clktime - startsec) * 1000 +
           ((clkticks - startms) * 1000) / CLKTICKS_PER_SEC);
    printf("rtt min/avg/max = %d.%03d/", min / 1000, min % 1000);
    if (0 != recv)
    {
        printf("%d.%03d/", (total / recv) / 1000, (total / recv) % 1000);
    }
    else
    {
        printf("-/");
    }
    printf("%d.%03d ms\n", max / 1000, max % 1000);

    return SHELL_OK;
}
Example #9
0
/**
 * The comments for dhcpRecvReply() apply, but for do_dhcpRecvReply() there is
 * no timeout and it is executed in a separate thread; furthermore, since this
 * thread can be killed at any time, it must use the memory passed in the @p pkt
 * parameter rather than allocating its own memory.
 */
static thread do_dhcpRecvReply(int descrp, struct dhcpData *data,
                               struct packet *pkt)
{
    const struct etherPkt *epkt;
    const struct ipv4Pkt *ipv4;
    const struct udpPkt *udp;
    const struct dhcpPkt *dhcp;
    const uchar *opts;
    uint maxlen;
    int found_msg;
    int retval;
    const uchar *gatewayptr;
    const uchar *maskptr;
    const uchar *opts_end;
    uint serverIpv4Addr;
    int mtu;
    int linkhdrlen;

    mtu = control(descrp, NET_GET_MTU, 0, 0);
    linkhdrlen = control(descrp, NET_GET_LINKHDRLEN, 0, 0);
    if (SYSERR == mtu || SYSERR == linkhdrlen)
    {
        return SYSERR;
    }

    maxlen = linkhdrlen + mtu;

    /* Receive packets until we find a response we're waiting for.  */

next_packet:
    do
    {
        /* Receive next packet from the network device.  */
        int len = read(descrp, pkt->data, maxlen);
        if (len == SYSERR || len <= 0)
        {
            data->recvStatus = SYSERR;
            return SYSERR;
        }

        pkt->len = len;
        DHCP_TRACE("Received packet (len=%u).", pkt->len);

        /* Make sure packet is at least the minimum length of a DHCP packet.  */
        if (pkt->len < (ETH_HDR_LEN + IPv4_HDR_LEN +
                        UDP_HDR_LEN + DHCP_HDR_LEN))
        {
            DHCP_TRACE("Too short to be a DHCP packet.");
            goto next_packet;
        }

        /* Set up header pointers  */
        epkt = (const struct etherPkt *)pkt->data;
        ipv4 = (const struct ipv4Pkt *)epkt->data;
        udp = (const struct udpPkt *)ipv4->opts;
        dhcp = (const struct dhcpPkt *)udp->data;

        /* DHCP packets must be type IPv4, protocol UDP, UDP dest port DHCPC,
         * and UDP source port DHCPS.
         *
         * Also check the DHCP header:  Is it actually a reply to this client?
         * */
        if ((ETHER_TYPE_IPv4 != net2hs(epkt->type))
            || (IPv4_PROTO_UDP != ipv4->proto)
            || (UDP_PORT_DHCPC != net2hs(udp->dstPort))
            || (UDP_PORT_DHCPS != net2hs(udp->srcPort))
            || (DHCP_OP_REPLY != dhcp->op)
            || (data->cxid != net2hl(dhcp->xid)))
        {
            DHCP_TRACE("Not a DHCP reply to this client.");
            goto next_packet;
        }

        DHCP_TRACE("Received DHCP reply.");

    #if DHCP_DROP_PACKET_PERCENT != 0
        /* Stress testing.  */
        if (rand() % 100 < DHCP_DROP_PACKET_PERCENT)
        {
            DHCP_TRACE("WARNING: Ignoring valid DHCP packet for test purposes.");
            goto next_packet;
        }
    #endif

        /* We got a valid DHCP reply.  Now parse the DHCP options.  This needs
         * to be done carefully to avoid overrunning the packet buffer if the
         * options data is invalid.  */
        opts = dhcp->opts;
        maskptr = NULL;
        gatewayptr = NULL;
        serverIpv4Addr = 0;
        opts_end = opts + (pkt->len - (ETH_HDR_LEN + IPv4_HDR_LEN +
                                       UDP_HDR_LEN + DHCP_HDR_LEN));
        found_msg = -1;
        for (;;)
        {
            uchar opt, len;

            /* Get the next option type.  */
            if (opts >= opts_end)
            {
                DHCP_TRACE("Invalid DHCP options.");
                goto next_packet;
            }
            opt = *opts++;

            /* Break on DHCP_OPT_END (marks end of DHCP options).  */
            if (DHCP_OPT_END == opt)
            {
                DHCP_TRACE("Reached DHCP_OPT_END.");
                break;
            }

            /* Get length of the data for this option.  */
            if (opts >= opts_end)
            {
                DHCP_TRACE("Invalid DHCP options.");
                goto next_packet;
            }
            len = *opts++;

            if (opts + len >= opts_end)
            {
                DHCP_TRACE("Invalid DHCP options.");
                goto next_packet;
            }

            /* Process the specific DHCP option.  Ignore unrecognized options.
             * */
            switch (opt)
            {
            case DHCP_OPT_MSGTYPE:
                DHCP_TRACE("DHCP_OPT_MSGTYPE: %d", *opts);
                if (len >= 1)
                {
                    if ((DHCPC_STATE_SELECTING == data->state &&
                         *opts == DHCPOFFER)
                        || (DHCPC_STATE_REQUESTING == data->state &&
                            (DHCPACK == *opts || DHCPNAK == *opts)))
                    {
                        found_msg = *opts;
                    }
                }
                break;

            case DHCP_OPT_SUBNET:
                if (len >= IPv4_ADDR_LEN)
                {
                    maskptr = opts;
                }
                break;

            case DHCP_OPT_GATEWAY:
                if (len >= IPv4_ADDR_LEN)
                {
                    gatewayptr = opts;
                }
                break;

            case DHCP_OPT_SERVER:
                if (len >= IPv4_ADDR_LEN)
                {
                    /* Server Identifier option.  */
                    serverIpv4Addr = ((uint)opts[0] << 24) |
                                     ((uint)opts[1] << 16) |
                                     ((uint)opts[2] << 8) |
                                     ((uint)opts[3] << 0);
                }
                break;
            }

            /* Advance by the length of the option's data.  */
            opts += len;
        }
    } while (found_msg < 0);

    /* We received a reply of at least the right type as one we were looking
     * for.  */

    /* Don't consider a DHCPACK reply to be valid unless it provided a subnet
     * mask.  */
    if (DHCPACK == found_msg && NULL == maskptr)
    {
        DHCP_TRACE("Ignoring DHCPACK (no subnet mask provided).");
        goto next_packet;
    }

    /* Note: The server's IP address is supposed to be specified in the Server
     * Identifier option, *not* in the siaddr (Server IP Address) field.  This
     * is because, somewhat unintuitively, siaddr is used for the address of the
     * next server in the bootstrap process, which may not be the same as the
     * DHCP server.  But if the Server Identifier option wasn't present, use
     * siaddr anyway.  */
    if (0 == serverIpv4Addr)
    {
        serverIpv4Addr = net2hl(dhcp->siaddr);
        if (0 == serverIpv4Addr)
        {
            DHCP_TRACE("Server IP address empty.");
            goto next_packet;
        }
    }

    if (DHCPOFFER == found_msg)
    {
        /* DHCPOFFER:  Remember offer and server addresses.  */
        data->serverIpv4Addr = serverIpv4Addr;
        data->offeredIpv4Addr = net2hl(dhcp->yiaddr);
        memcpy(data->serverHwAddr, epkt->src, ETH_ADDR_LEN);
        retval = OK;
    }
    else
    {
        /* Received DHCPACK or DHCPNAK.  Ensure it's from the same server to
         * which we sent the DHCPREQUEST; if not, ignore the packet.  */
        if (serverIpv4Addr != data->serverIpv4Addr)
        {
            DHCP_TRACE("Reply from wrong server.");
            goto next_packet;
        }

        if (DHCPNAK == found_msg)
        {
            /* DHCPNAK:  Return error to make client try DHCPDISCOVER again  */
            retval = SYSERR;
        }
        else
        {
            /* DHCPACK:  Set network interface addresses  */
            data->ip.type = NETADDR_IPv4;
            data->ip.len = IPv4_ADDR_LEN;
            /* yiaddr in a DHCPACK should be the same as the yiaddr stored from
             * the DHCPOFFER, but using the value in DHCPACK is preferable since
             * it's the value the server thinks it assigned.  */
            memcpy(data->ip.addr, &dhcp->yiaddr, IPv4_ADDR_LEN);
            data->clientIpv4Addr = net2hl(dhcp->yiaddr);

            data->mask.type = NETADDR_IPv4;
            data->mask.len = IPv4_ADDR_LEN;
            memcpy(data->mask.addr, maskptr, IPv4_ADDR_LEN);

            if (NULL != gatewayptr)
            {
                data->gateway.type = NETADDR_IPv4;
                data->gateway.len = IPv4_ADDR_LEN;
                memcpy(data->gateway.addr, gatewayptr, IPv4_ADDR_LEN);
            }

            /* If provided in the DHCPACK, set the address of next server and
             * the boot file (e.g. for TFTP).  */
            if (0 != dhcp->siaddr)
            {
                data->next_server.type = NETADDR_IPv4;
                data->next_server.len = IPv4_ADDR_LEN;
                memcpy(data->next_server.addr, &dhcp->siaddr, IPv4_ADDR_LEN);
            }
            if ('\0' != dhcp->file[0])
            {
                memcpy(data->bootfile, dhcp->file, sizeof(data->bootfile) - 1);
            }
        #ifdef ENABLE_DHCP_TRACE
            {
                char str_addr[24];
                netaddrsprintf(str_addr, &data->ip);
                DHCP_TRACE("Set ip=%s", str_addr);

                netaddrsprintf(str_addr, &data->mask);
                DHCP_TRACE("Set mask=%s", str_addr);

                if (NULL != gatewayptr)
                {
                    netaddrsprintf(str_addr, &data->gateway);
                    DHCP_TRACE("Set gateway=%s", str_addr);
                }
                else
                {
                    DHCP_TRACE("No gateway.");
                }

                netaddrsprintf(str_addr, &data->next_server);
                DHCP_TRACE("TFTP server=%s", str_addr);
                DHCP_TRACE("Bootfile=%s", data->bootfile);
            }
        #endif
            retval = OK;
        }
    }
    data->recvStatus = retval;
    return retval;
}
Example #10
0
/**
 * @ingroup raw
 *
 * Locate the raw socket for a packet
 * @param src source IP address of the packet
 * @param dst destination IP address of the packet
 * @param proto protocol of the packet
 * @return most completely matched socket, NULL if no match
 */
struct raw *rawDemux(struct netaddr *src, struct netaddr *dst,
                     ushort proto)
{
    struct raw *rawptr;
    uint i;
    uint level;

    rawptr = NULL;
    level = 0;

#ifdef TRACE_RAW
    char strA[20], strB[20];
    netaddrsprintf(strA, src);
    netaddrsprintf(strB, dst);
    RAW_TRACE("Demultiplex proto %d src %s dst %s", proto, strA, strB);
#endif

    /* Cycle through all RAW devices to find the best match */
    for (i = 0; i < NRAW; i++)
    {
        if (RAW_ALLOC == rawtab[i].state)
        {
#ifdef TRACE_RAW
            netaddrsprintf(strA, &rawtab[i].localip);
            netaddrsprintf(strB, &rawtab[i].remoteip);
            RAW_TRACE("Socket %d proto %d local IP %s remote IP %s", i,
                      rawtab[i].proto, strA, strB);
#endif

            /* Full match is the best */
            if (level < 4
                && (proto == rawtab[i].proto)
                && (netaddrequal(src, &rawtab[i].remoteip))
                && (netaddrequal(dst, &rawtab[i].localip)))
            {
                rawptr = &rawtab[i];
                level = 4;
                RAW_TRACE("Level 4 match, socket %d", i);
            }

            /* Protocol and remote IP match is second */
            if (level < 3
                && (proto == rawtab[i].proto)
                && (((netaddrequal(src, &rawtab[i].remoteip))
                     && (NULL == rawtab[i].localip.type))
                    || ((netaddrequal(dst, &rawtab[i].localip))
                        && (NULL == rawtab[i].remoteip.type))))
            {
                rawptr = &rawtab[i];
                level = 3;
                RAW_TRACE("Level 3 match, socket %d", i);
            }

            /* Protocol match is third */
            if (level < 2
                && (proto == rawtab[i].proto)
                && (NULL == rawtab[i].remoteip.type)
                && (NULL == rawtab[i].localip.type))
            {
                rawptr = &rawtab[i];
                level = 2;
                RAW_TRACE("Level 2 match, socket %d", i);
            }

            /* All protocols is last */
            if (level < 1
                && (NULL == rawtab[i].proto)
                && (NULL == rawtab[i].remoteip.type)
                && (NULL == rawtab[i].localip.type))
            {
                rawptr = &rawtab[i];
                level = 1;
                RAW_TRACE("Level 1 match, socket %d", i);
            }

        }
    }

    return rawptr;
}
Example #11
0
/**
 * @ingroup shell
 *
 * Shell command (route) displays routing information.
 * @param nargs number of arguments
 * @param args  array of arguments
 * @return non-zero value on error
 */
shellcmd xsh_route(int nargs, char *args[])
{
    int i;
    char c[32];
    device *pdev;
    struct netif *netptr;

    struct netaddr dst;
    struct netaddr mask;
    struct netaddr gateway;

    /* Check for correct number of arguments */
    if (nargs > 6)
    {
        fprintf(stderr, "%s: too many arguments\n", args[0]);
        fprintf(stderr, "Try '%s --help' for more information\n",
                args[0]);
        return SYSERR;
    }

    /* Output help, if '--help' argument was supplied */
    if (nargs == 2 && strcmp(args[1], "--help") == 0)
    {
        printf("\nUsage: %s ", args[0]);
        printf("[add <DESTINATION> <GATEWAY> <MASK> <INTERFACE>] ");
        printf("[del <DESTINATION>]\n\n");
        printf("Description:\n");
        printf("\tDisplays routing table\n");
        printf("Options:\n");
        printf("\tadd <DESTINATION> <GATEWAY> <MASK> <INTERFACE>\n");
        printf("\t\t\t\tadd route entry into table.\n");
        printf("\t\t\t\t(<INTERFACE> must be in all caps.)\n");
        printf("\tdel <DESTINATION>");
        printf("\tdelete route entry from table.\n");
        printf("\t--help\t\t\tdisplay this help and exit\n");
        return OK;
    }

    if (nargs == 6 && strcmp(args[1], "add") == 0)
    {
        /* Parse destination */
        if (strcmp(args[2], "default") == 0)
        {
            args[2] = "";
        }
        if (SYSERR == dot2ipv4(args[2], &dst))
        {
            fprintf(stderr, "%s is not a valid IPv4 address.\n", args[2]);
            return SYSERR;
        }

        /* Parse gateway */
        if (SYSERR == dot2ipv4(args[3], &gateway))
        {
            fprintf(stderr, "%s is not a valid IPv4 address.\n", args[3]);
            return SYSERR;
        }

        /* Parse mask */
        if (SYSERR == dot2ipv4(args[4], &mask))
        {
            fprintf(stderr, "%s is not a valid IPv4 address mask.\n",
                    args[4]);
            return SYSERR;
        }

        /* Parse interface */
#if NNETIF
        for (i = 0; i < NNETIF; i++)
        {
            if (NET_ALLOC == netiftab[i].state)
            {
                netptr = &netiftab[i];
                pdev = (device *)&devtab[netptr->dev];
                if (strcmp(pdev->name, args[5]) == 0)
                {
                    if (SYSERR == rtAdd(&dst, &gateway, &mask, netptr))
                    {
                        fprintf(stderr, "Failed to add route.\n");
                        return SYSERR;
                    }
                    return OK;
                }
            }
        }
#endif
        fprintf(stderr, "%s is not a valid network interface.\n",
                args[5]);
        return SYSERR;
    }
    else if (nargs == 3 && strcmp(args[1], "del") == 0)
    {
        /* Parse destination */
        if (strcmp(args[2], "default") == 0)
        {
            args[2] = "";
        }
        if (SYSERR == dot2ipv4(args[2], &dst))
        {
            fprintf(stderr, "%s is not a valid IPv4 address.\n", args[2]);
            return SYSERR;
        }

        if (SYSERR == rtRemove(&dst))
        {
            fprintf(stderr, "Failed to delete route.\n");
            return SYSERR;
        }
        return OK;
    }

    printf
        ("Destination     Gateway         Mask            Interface\r\n");

    for (i = 0; i < RT_NENTRY; i++)
    {
        if (RT_USED == rttab[i].state)
        {
            if (0 == rttab[i].masklen)
                sprintf(c, "default");
            else
                netaddrsprintf(c, &rttab[i].dst);
            printf("%-16s", c);

            netaddrsprintf(c, &rttab[i].gateway);
            if (strcmp(c, "NULL") == 0)
                sprintf(c, "*");
            printf("%-16s", c);

            netaddrsprintf(c, &rttab[i].mask);
            printf("%-16s", c);

            netptr = rttab[i].nif;
            pdev = (device *)&devtab[netptr->dev];
            printf("%s\r\n", pdev->name);
        }
    }

    return 0;
}
Example #12
0
/**
 * @ingroup tftp
 *
 * Download a file from a remote server using TFTP and passes its contents,
 * block-by-block, to a callback function.  This callback function can do
 * whatever it wants with the file data, such as store it all into a buffer or
 * write it to persistent storage.
 *
 * @param[in] filename
 *      Name of the file to download.
 * @param[in] local_ip
 *      Local protocol address to use for the connection.
 * @param[in] server_ip
 *      Remote protocol address to use for the connection (address of TFTP
 *      server).
 * @param[in] recvDataFunc
 *      Callback function that will be passed the file data block-by-block.  For
 *      each call of the callback function, the @p data (first) argument will be
 *      set to a pointer to the next block of data and the @p len (second)
 *      argument will be set to the block's length.  All data blocks will be the
 *      same size, except possibly the last, which can be anywhere from 0 bytes
 *      up to the size of the previous block(s) if any.
 *      <br/>
 *      In the current implementation, the block size (other than possibly for
 *      the last block) is fixed at 512 bytes.  However, implementations of this
 *      callback SHOULD handle larger block sizes since tftpGet() could be
 *      extended to support TFTP block size negotiation.
 *      <br/>
 *      This callback is expected to return ::OK if successful.  If it does not
 *      return ::OK, the TFTP transfer is aborted and tftpGet() returns this
 *      value.
 * @param[in] recvDataCtx
 *      Extra parameter that will be passed literally to @p recvDataFunc.
 *
 * @return
 *      ::OK on success; ::SYSERR if the TFTP transfer times out or fails, or if
 *      one of several other errors occur; or the value returned by @p
 *      recvDataFunc, if it was not ::OK.
 */
syscall tftpGet(const char *filename, const struct netaddr *local_ip,
                const struct netaddr *server_ip, tftpRecvDataFunc recvDataFunc,
                void *recvDataCtx)
{
    int udpdev;
    int udpdev2;
    int send_udpdev;
    int recv_udpdev;
    int retval;
    tid_typ recv_tid;
    uint num_rreqs_sent;
    uint block_recv_tries;
    uint next_block_number;
    ushort localpt;
    uint block_max_end_time = 0;  /* This value is not used, but
                                     gcc fails to detect it.  */
    uint block_attempt_time;
    struct tftpPkt pkt;

    /* Make sure the required parameters have been specified.  */
    if (NULL == filename || NULL == local_ip ||
        NULL == server_ip || NULL == recvDataFunc)
    {
        TFTP_TRACE("Invalid parameter.");
        return SYSERR;
    }

#ifdef ENABLE_TFTP_TRACE
    {
        char str_local_ip[20];
        char str_server_ip[20];
        netaddrsprintf(str_local_ip, local_ip);
        netaddrsprintf(str_server_ip, server_ip);
        TFTP_TRACE("Downloading %s from %s (using local_ip=%s)",
                   filename, str_server_ip, str_local_ip);
    }
#endif

    /* Allocate and open a UDP device (socket) to communicate with the TFTP
     * server on.  The local and remote protocol addresses are specified as
     * required parameters to this function.  The local port, which corresponds
     * to the client's TFTP Transfer Identifier (TID) as per RFC 1350, must be
     * allocated randomly; the UDP code handles this if 0 is passed as the local
     * port.  The remote port is always initially the well-known TFTP port (69),
     * but after receiving the first data packet it must be changed to the port
     * on which the server actually responded.
     *
     * However... the last point about the server responding on a different port
     * (which is unavoidable; it's how TFTP is designed) complicates things
     * significantly.  This is because the UDP code will *not* route the
     * server's response to the initial UDP device, as this device will be bound
     * to port 69, not the actual port the server responded on.  To work around
     * this problem without manually dealing with UDP headers, we create a
     * *second* UDP device, which initially listens on the port from which the
     * client sends the initial RRQ, but is initially *not* bound to any remote
     * port or address.  We then set UDP_FLAG_BINDFIRST on this second UDP
     * device so that the remote port and address are automatically filled in
     * when the response from the server is received.  Further packets sent from
     * the server are then received on this second UDP device, while further
     * packets sent from the client are then sent over this second UDP device
     * rather than the first since the second has the remote port correctly set.
     * */

    udpdev = udpAlloc();

    if (SYSERR == udpdev)
    {
        TFTP_TRACE("Failed to allocate first UDP device.");
        return SYSERR;
    }

    if (SYSERR == open(udpdev, local_ip, server_ip, 0, UDP_PORT_TFTP))
    {
        TFTP_TRACE("Failed to open first UDP device.");
        udptab[udpdev - UDP0].state = UDP_FREE;
        return SYSERR;
    }

    localpt = udptab[udpdev - UDP0].localpt;

    udpdev2 = udpAlloc();
    if (SYSERR == udpdev2)
    {
        TFTP_TRACE("Failed to allocate second UDP device.");
        retval = SYSERR;
        goto out_close_udpdev;
    }

    if (SYSERR == open(udpdev2, local_ip, NULL, localpt, 0))
    {
        TFTP_TRACE("Failed to open second UDP device.");
        retval = SYSERR;
        udptab[udpdev2 - UDP0].state = UDP_FREE;
        goto out_close_udpdev;
    }

    send_udpdev = udpdev;
    recv_udpdev = udpdev2;

    /* See lengthy comment above for explanation of this flag.  */
    control(recv_udpdev, UDP_CTRL_SETFLAG, UDP_FLAG_BINDFIRST, 0);

    TFTP_TRACE("Using UDP%d (for initial send) "
               "and UDP%d (for binding reply), client port %u",
               send_udpdev - UDP0, recv_udpdev - UDP0, localpt);

    /* Create receive thread.  This is a workaround to avoid having the
     * currently executing thread call read() on the UDP device, which can block
     * indefinitely.  */
    recv_tid = create(tftpRecvPackets, TFTP_RECV_THR_STK,
                      TFTP_RECV_THR_PRIO, "tftpRecvPackets", 3,
                      recv_udpdev, &pkt, gettid());
    if (isbadtid(recv_tid))
    {
        TFTP_TRACE("Failed to create TFTP receive thread.");
        retval = SYSERR;
        goto out_close_udpdev2;
    }
    ready(recv_tid, RESCHED_NO);

    /* Begin the download by requesting the file.  */
    retval = tftpSendRRQ(send_udpdev, filename);
    if (SYSERR == retval)
    {
        retval = SYSERR;
        goto out_kill_recv_thread;
    }
    num_rreqs_sent = 1;
    next_block_number = 1;

    /* Loop until file is fully downloaded or an error condition occurs.  The
     * basic idea is that the client receives DATA packets one-by-one, each of
     * which corresponds to the next block of file data, and the client ACK's
     * each one before the server sends the next.  But the actual code below is
     * a bit more complicated as it must handle timeouts, retries, invalid
     * packets, etc.  */
    block_recv_tries = 0;
    for (;;)
    {
        ushort opcode;
        ushort recv_block_number;
        struct netaddr *remote_address;
        bool wrong_source;
        ushort block_nbytes;

        /* Handle bookkeeping for timing out.  */

        block_attempt_time = clktime;
        if (block_recv_tries == 0)
        {
            uint timeout_secs;

            if (next_block_number == 1)
            {
                timeout_secs = TFTP_INIT_BLOCK_TIMEOUT;
            }
            else
            {
                timeout_secs = TFTP_BLOCK_TIMEOUT;
            }
            block_max_end_time = block_attempt_time + timeout_secs;
        }

        if (block_attempt_time <= block_max_end_time)
        {
            /* Try to receive the block using the appropriate timeout.  The
             * actual receive is done by another thread, executing
             * tftpRecvPacket(s).  */
            TFTP_TRACE("Waiting for block %u", next_block_number);
            block_recv_tries++;
            send(recv_tid, 0);
            retval = recvtime(1000 * (block_max_end_time -
                                      block_attempt_time) + 500);
        }
        else
        {
            /* Timeout was reached.  */
            retval = TIMEOUT;
        }

        /* Handle timeout.  */
        if (TIMEOUT == retval)
        {
            TFTP_TRACE("Receive timed out.");

            /* If the client is still waiting for the very first reply from the
             * server, don't fail on the first timeout; instead wait until the
             * client has had the chance to re-send the RRQ a few times.  */
            if (next_block_number == 1 &&
                num_rreqs_sent < TFTP_INIT_BLOCK_MAX_RETRIES)
            {
                TFTP_TRACE("Trying RRQ again (try %u of %u)",
                           num_rreqs_sent + 1, TFTP_INIT_BLOCK_MAX_RETRIES);
                retval = tftpSendRRQ(send_udpdev, filename);
                if (SYSERR == retval)
                {
                    break;
                }
                block_recv_tries = 0;
                num_rreqs_sent++;
                continue;
            }

            /* Timed out for real; clean up and return failure status.  */
            retval = SYSERR;
            break;
        }

        /* Return failure status if packet was not otherwise successfully
         * received for some reason.  */
        if (SYSERR == retval)
        {
            TFTP_TRACE("UDP device or message passing error; aborting.");
            break;
        }

        /* Otherwise, 'retval' is the length of the received TFTP packet.  */

        /* Begin extracting information from and validating the received packet.
         * What we're looking for is a well-formed TFTP DATA packet from the
         * correct IP address, where the block number is either that of the next
         * block or that of the previous block.  The very first block needs some
         * special handling, however; in particular, there is no previous block
         * in that case, and the remote network address needs to be checked to
         * verify the socket was actually bound to the server's network address
         * as expected.
         */
        remote_address = &udptab[recv_udpdev - UDP0].remoteip;
        opcode = net2hs(pkt.opcode);
        recv_block_number = net2hs(pkt.DATA.block_number);
        wrong_source = !netaddrequal(server_ip, remote_address);

        if (wrong_source || retval < 4 || TFTP_OPCODE_DATA != opcode ||
            (recv_block_number != (ushort)next_block_number &&
             (next_block_number == 1 ||
              recv_block_number != (ushort)next_block_number - 1)))
        {
            /* Check for TFTP ERROR packet  */
            if (!wrong_source && (retval >= 2 && TFTP_OPCODE_ERROR == opcode))
            {
                TFTP_TRACE("Received TFTP ERROR opcode packet; aborting.");
                retval = SYSERR;
                break;
            }
            TFTP_TRACE("Received invalid or unexpected packet.");

            /* If we're still waiting for the first valid reply from the server
             * but the bound connection is *not* from the server, reset the
             * BINDFIRST flag.  */
            if (wrong_source && next_block_number == 1)
            {
                irqmask im;
                TFTP_TRACE("Received packet is from wrong source; "
                           "re-setting bind flag.");
                im = disable();
                control(recv_udpdev, UDP_CTRL_BIND, 0, (long)NULL);
                control(recv_udpdev, UDP_CTRL_SETFLAG, UDP_FLAG_BINDFIRST, 0);
                restore(im);
            }

            /* Ignore the bad packet and try receiving again.  */
            continue;
        }

        /* Received packet is a valid TFTP DATA packet for either the next block
         * or the previous block.  */


    #if TFTP_DROP_PACKET_PERCENT != 0
        /* Stress testing.  */
        if (rand() % 100 < TFTP_DROP_PACKET_PERCENT)
        {
            TFTP_TRACE("WARNING: Ignoring valid TFTP packet for test purposes.");
            continue;
        }
    #endif

        /* If this is the first response from the server, set the actual port
         * that it responded on.  */
        if (next_block_number == 1)
        {
            send_udpdev = recv_udpdev;
            TFTP_TRACE("Server responded on port %u; bound socket",
                       udptab[recv_udpdev - UDP0].remotept);
        }

        /* Handle receiving the next data block.  */
        block_nbytes = TFTP_BLOCK_SIZE;
        if (recv_block_number == (ushort)next_block_number)
        {
            block_nbytes = retval - 4;
            TFTP_TRACE("Received block %u (%u bytes)",
                       recv_block_number, block_nbytes);

            /* Feed received data into the callback function.  */
            retval = (*recvDataFunc)(pkt.DATA.data, block_nbytes,
                                     recvDataCtx);
            /* Return if callback did not return OK.  */
            if (OK != retval)
            {
                break;
            }
            next_block_number++;
            block_recv_tries = 0;
        }

        /* Acknowledge the block received.  */
        retval = tftpSendACK(send_udpdev, recv_block_number);

        /* A TFTP Get transfer is complete when a short data block has been
         * received.   Note that it doesn't really matter from the client's
         * perspective whether the last data block is acknowledged or not;
         * however, the server would like to know so it doesn't keep re-sending
         * the last block.  For this reason we did send the final ACK packet but
         * will ignore failure to send it.  */
        if (block_nbytes < TFTP_BLOCK_SIZE)
        {
            retval = OK;
            break;
        }

        /* Break if sending the ACK failed.  */
        if (SYSERR == retval)
        {
            break;
        }
    }
    /* Clean up and return.  */
out_kill_recv_thread:
    kill(recv_tid);
out_close_udpdev2:
    close(udpdev2);
out_close_udpdev:
    close(udpdev);
    return retval;
}
Example #13
0
static void kexec_from_network(int netdev, char *boot)
{
#if defined(WITH_DHCPC) && NETHER != 0
    struct dhcpData data;
    int result;
    const struct netaddr *gatewayptr;
    struct netif *nif;
    void *kernel;
    uint size;
    char str_ip[20];
    char str_mask[20];
    char str_gateway[20];
    const char *netdevname = devtab[netdev].name;

    /* Bring network interface (if any) down.  */
    netDown(netdev);

    /* Run DHCP client on the device for at most 10 seconds.  */
    printf("Running DHCP on %s\n", netdevname);
    result = dhcpClient(netdev, 10, &data);
    if (OK != result)
    {
        fprintf(stderr, "ERROR: DHCP failed.\n");
        return;
    }

    /* Ensure the DHCP server provided the boot filename and TFTP server IP
     * address.  */
    if (('\0' == data.bootfile[0] || 0 == data.next_server.type) && boot == NULL)
    {
        fprintf(stderr, "ERROR: DHCP server did not provide boot file "
                "and TFTP server address.\n");
        return;
    }

    /* Bring up the network interface.  */
    netaddrsprintf(str_ip, &data.ip);
    netaddrsprintf(str_mask, &data.mask);
    if (0 != data.gateway.len)
    {
        netaddrsprintf(str_gateway, &data.gateway);
        printf("Bringing up %s as %s with mask %s (gateway %s)\n",
               netdevname, str_ip, str_mask, str_gateway);
        gatewayptr = &data.gateway;
    }
    else
    {
        printf("Bringing up %s as %s with mask %s (no gateway)\n",
               netdevname, str_ip, str_mask);
        gatewayptr = NULL;
    }
    result = netUp(netdev, &data.ip, &data.mask, gatewayptr);
    if (OK != result)
    {
        fprintf(stderr, "ERROR: failed to bring up %s.\n", netdevname);
        return;
    }
    nif = netLookup(netdev);

    /* Download new kernel using TFTP.  */
    netaddrsprintf(str_ip, &data.next_server);
	 if (boot == NULL) {
    	printf("Downloading bootfile \"%s\" from TFTP server %s\n",
      	     data.bootfile, str_ip);
    	kernel = (void*)tftpGetIntoBuffer(data.bootfile, &nif->ip,
                                        &data.next_server, &size);
	 }else {
    	printf("Downloading bootfile \"%s\" from TFTP server %s\n",
      	     boot, str_ip);
    	kernel = (void*)tftpGetIntoBuffer(boot, &nif->ip,
                                        &data.next_server, &size);
	 }

    if (SYSERR == (int)kernel)
    {
        fprintf(stderr, "ERROR: TFTP failed.\n");
        return;
    }

    /* Execute the new kernel.  */
    printf("Executing new kernel (size=%u)\n", size);
    sleep(100);  /* Wait just a fraction of a second for printf()s to finish
                    (no guarantees though).  */
    kexec(kernel, size);

    fprintf(stderr, "ERROR: kexec() returned!\n");

#else /* WITH_DHCPC && NETHER != 0 */
    fprintf(stderr,
            "ERROR: Network boot is not supported in this configuration.\n"
            "       Please make sure you have enabled one or more network\n"
            "       devices, along with the DHCP and TFTP clients.\n");
#endif /* !(WITH_DHCPC && NETHER != 0) */
}