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); }
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; }
/** * 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; }
/** * 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; }
/** * 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); }
/** * 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(©, 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, ©.localip); printf("Local Port: %-5d IP: %-15s\n", copy.localpt, strA); printf(" "); netaddrsprintf(strA, ©.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; }
/** * 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; }
/** * @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; }
/** * 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; }
/** * @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; }
/** * @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; }
/** * @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; }
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) */ }