/*------------------------------------------------------------------------ * udpnet2h - convert UDP header fields from net to host byte order *------------------------------------------------------------------------ */ void udpnet2h(struct udp *pudp) { pudp->u_src = net2hs(pudp->u_src); pudp->u_dst = net2hs(pudp->u_dst); pudp->u_len = net2hs(pudp->u_len); }
/*------------------------------------------------------------------------ * ipnet2h - convert an IP packet header from net to host byte order *------------------------------------------------------------------------ */ struct ip * ipnet2h(struct ip *pip) { /* NOTE: does not include IP options */ pip->ip_len = net2hs(pip->ip_len); pip->ip_id = net2hs(pip->ip_id); pip->ip_fragoff = net2hs(pip->ip_fragoff); return pip; }
//------------------------------------------------------------------------ // rarp_in - handle RARP packet coming in from Ethernet network //------------------------------------------------------------------------ int rarp_in(struct epacket *packet, int dev) { int ps; int pid; int ret; struct arppak *apacptr; struct etblk *etptr; apacptr = (struct arppak *)packet->ep_data; ret = SYSERR; if (net2hs(apacptr->ar_op) == AR_RRLY) { etptr = (struct etblk *)devtab[dev].iobuf; if (memcmp(apacptr->ar_tha, etptr->etpaddr, EPADLEN) == 0) { memmove(Net.myaddr, apacptr->ar_tpa, IPLEN); netnum(Net.mynet, Net.myaddr); ps = disable(); Net.mavalid = TRUE; pid = Arp.rarppid; if (!isbadpid(pid)) { Arp.rarppid = BADPID; send(pid, OK); } restore(ps); } ret = OK; } freebuf(packet); return ret; }
/*------------------------------------------------------------------------ * tcpsmss - set sender MSS from option in incoming segment *------------------------------------------------------------------------ */ int tcpsmss(struct tcb *ptcb, struct tcp *ptcp, u_char *popt) { unsigned mss, len; len = *++popt; ++popt; /* skip length field */ if ((ptcp->tcp_code & TCPF_SYN) == 0) return len; switch (len-2) { /* subtract kind & len */ case sizeof(char): mss = *popt; break; case sizeof(short): mss = net2hs(*(unsigned short *)popt); break; case sizeof(long): mss = net2hl(*(unsigned long *)popt); break; default: mss = ptcb->tcb_smss; break; } mss -= TCPMHLEN; /* save just the data buffer size */ if (ptcb->tcb_smss) ptcb->tcb_smss = min(mss, ptcb->tcb_smss); else ptcb->tcb_smss = mss; return len; }
/** * Send an ICMP Echo (Ping) Request. * @param dst destination address * @param id ping stream identifier * @param seq sequence number * @return OK if packet was sent, otherwise SYSERR */ syscall icmpEchoRequest(struct netaddr *dst, ushort id, ushort seq) { struct packet *pkt = NULL; struct icmpEcho *echo = NULL; int result = OK; irqmask im; ICMP_TRACE("echo request(%d, %d)", id, seq); pkt = netGetbuf(); if (SYSERR == (int)pkt) { ICMP_TRACE("Failed to acquire packet buffer"); return SYSERR; } pkt->len = sizeof(struct icmpEcho); pkt->curr -= pkt->len; echo = (struct icmpEcho *)pkt->curr; echo->id = hs2net(id); echo->seq = hs2net(seq); /* Our optional data payload includes room for the departure */ /* and arrival timestamps, in seconds, milliseconds, and */ /* clock cycles. */ im = disable(); echo->timecyc = hl2net(clkcount()); echo->timetic = hl2net(clkticks); echo->timesec = hl2net(clktime); restore(im); echo->arrivcyc = 0; echo->arrivtic = 0; echo->arrivsec = 0; ICMP_TRACE("Sending Echo Request id = %d, seq = %d, time = %d.%d", net2hs(echo->id), net2hs(echo->seq), net2hl(echo->timesec), net2hl(echo->timetic)); result = icmpSend(pkt, ICMP_ECHO, 0, sizeof(struct icmpEcho), dst); netFreebuf(pkt); return result; }
/*------------------------------------------------------------------------ * ospfcheck - check if a packet is a valid OSPF packet *------------------------------------------------------------------------ */ int ospfcheck(struct ep *pep) { struct ip *pip = (struct ip *)pep->ep_data; struct ospf *po = (struct ospf *)((char *)pip + IP_HLEN(pip)); struct ospf_if *pif = &ospf_if[pep->ep_ifn]; if (pif->if_state == IFS_DOWN) return FALSE; if (po->ospf_version != OSPF_VERSION) return FALSE; if (net2hs(po->ospf_authtype) != pif->if_area->ar_authtype) return FALSE; if (pif->if_area->ar_authtype && memcmp(po->ospf_auth, pif->if_area->ar_auth, AUTHLEN)) return FALSE; memset(po->ospf_auth, 0, AUTHLEN); if (cksum((WORD *)po, net2hs(po->ospf_len))) return FALSE; return TRUE; }
/** * 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); }
/** * 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; }
static void snoopPrintTcpPort(ushort port, char *descrp) { switch (net2hs(port)) { case TCP_PORT_HTTP: sprintf(descrp, "(HTTP)"); break; case TCP_PORT_TELNET: sprintf(descrp, "(Telnet)"); break; default: sprintf(descrp, ""); break; } }
//------------------------------------------------------------------------ // ip_in - handle IP packet coming in from the network //------------------------------------------------------------------------ int ip_in(struct epacket *packet, int icmpp, int lim) { struct udp *udpptr; struct ip *ipptr; struct netq *nqptr; int dport; int i; int to; int ps; ipptr = (struct ip *)packet->ep_data; switch (ipptr->i_proto) { case IPRO_ICMP: // ICMP: pass to icmp input routine return icmp_in(packet, icmpp, lim); case IPRO_UDP: // UDP: demultiplex based on UDP "port" udpptr = (struct udp *)ipptr->i_data; dport = net2hs(udpptr->u_dport); for (i = 0; i < NETQS; i++) { nqptr = &Net.netqs[i]; if (nqptr->uport == dport) { // drop instead of blocking on psend if (pcount(nqptr->xport) >= NETQLEN) { Net.ndrop++; Net.nover++; freebuf(packet); return SYSERR; } psend(nqptr->xport, (uword)packet); ps = disable(); to = nqptr->pid; if (!isbadpid(to)) { nqptr->pid = BADPID; send(to, OK); } restore(ps); return OK; } } break; default: break; } Net.ndrop++; freebuf(packet); return OK; }
//------------------------------------------------------------------------ // netout - start network by finding address and forward IP packets //------------------------------------------------------------------------ PROCESS netout(int userpid, int icmpp) { struct epacket *packet; struct ip *ipptr; long tim; int len; char name[MNAMELEN]; IPaddr addr; getaddr(addr); gettime(&tim); getname(name, MNAMELEN); resume(userpid); while (TRUE) { packet = (struct epacket *)preceive(icmpp); ipptr = (struct ip *)packet->ep_data; memmove(addr, ipptr->i_dest, IPLEN); len = net2hs(ipptr->i_paclen) - IPHLEN; ipsend(addr, packet, len); } }
/** * 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; }
/*------------------------------------------------------------------------ * ip2name - return DNS name for a host given its IP address *------------------------------------------------------------------------ */ char * ip2name(IPaddr ip, char *nam) { char tmpstr[20]; /* temporary string buffer */ char *buf; /* buffer to hold domain query */ int dg, i; register char *p; register struct dn_mesg *dnptr; dnptr = (struct dn_mesg *) (buf = (char *) getmem(DN_MLEN)); *nam = NULLCH; dnptr->dn_id = 0; dnptr->dn_opparm = hs2net(DN_RD); dnptr->dn_qcount = hs2net(1); dnptr->dn_acount = dnptr->dn_ncount = dnptr->dn_dcount = 0; p = dnptr->dn_qaaa; /* Fill in question with ip[3].ip[2].ip[1].ip[0].in-addr.arpa */ for (i=3 ; i >= 0 ; i--) { sprintf(tmpstr, "%u", ((char *)&ip)[i] & LOWBYTE); dn_cat(p, tmpstr); } dn_cat(p, "in-addr"); dn_cat(p, "arpa"); *p++ = NULLCH; /* terminate name */ /* Add query type and query class fields to name */ ( (struct dn_qsuf *)p )->dn_type = hs2net(DN_QTPR); ( (struct dn_qsuf *)p )->dn_clas = hs2net(DN_QCIN); p += sizeof(struct dn_qsuf); /* Send query */ dg = open(UDP, NSERVER, ANYLPORT); control(dg, DG_SETMODE, DG_DMODE | DG_TMODE); if (write (dg, buf, p - buf) == SYSERR) panic("ip2name write(%d) failed\n", dg); i = read(dg, buf, DN_MLEN); if (i == SYSERR) panic("ip2name read(%d) failed\n", dg); if (i == TIMEOUT) { close(dg); return ip2dot(nam, ip); } close(dg); if (net2hs(dnptr->dn_opparm) & DN_RESP || net2hs(dnptr->dn_acount) <= 0) { freemem(buf, DN_MLEN); return ip2dot(nam, ip); } /* In answer, skip name and remainder of resource record header */ while (*p != NULLCH) if (*p & DN_CMPRS) /* compressed section of name */ *++p = NULLCH; else p += *p + 1; p += DN_RLEN + 1; /* Copy name to user */ *nam = NULLCH; while (*p != NULLCH) { if (*p & DN_CMPRS) p = buf + (net2hs(*(int *)p) & DN_CPTR); else { strncat(nam, p+1, *p); strcat(nam, "."); p += *p + 1; } } if (strlen(nam)) /* remove trailing dot */ nam[strlen(nam) - 1] = NULLCH; else ip2dot(nam, ip); freemem(buf, DN_MLEN); return nam; }
/** * @ingroup icmp * * Processes an incoming ICMP packet. * @param pkt pointer to the incoming packet * return OK if packet was processed succesfully, otherwise SYSERR */ syscall icmpRecv(struct packet *pkt) { struct icmpPkt *icmp; struct icmpEcho *echo; int id; /* Error check pointers */ if (NULL == pkt) { return SYSERR; } icmp = (struct icmpPkt *)pkt->curr; switch (icmp->type) { case ICMP_ECHOREPLY: ICMP_TRACE("Received Echo Reply"); echo = (struct icmpEcho *)icmp->data; id = net2hs(echo->id); if ((id >= 0) && (id < NTHREAD)) { int i; irqmask im; im = disable(); echo->arrivcyc = clkcount(); echo->arrivtic = clkticks; echo->arrivsec = clktime; for (i = 0; i < NPINGQUEUE; i++) { struct icmpEchoQueue *eq; eq = &echotab[i]; if (id == eq->tid) { ICMP_TRACE("Ping matches queue %d", i); if (((eq->head + 1) % NPINGHOLD) == eq->tail) { ICMP_TRACE("Queue full, discarding"); restore(im); netFreebuf(pkt); return SYSERR; } eq->pkts[eq->head] = pkt; eq->head = ((eq->head + 1) % NPINGHOLD); send(id, (message)pkt); restore(im); return OK; } } restore(im); } ICMP_TRACE("Reply id %d does not correspond to ping queue", id); netFreebuf(pkt); return SYSERR; case ICMP_ECHO: ICMP_TRACE("Enqueued Echo Request for daemon to reply"); mailboxSend(icmpqueue, (int)pkt); return OK; case ICMP_UNREACH: case ICMP_SRCQNCH: case ICMP_REDIRECT: case ICMP_TIMEEXCD: case ICMP_PARAMPROB: case ICMP_TMSTMP: case ICMP_TMSTMPREPLY: case ICMP_INFORQST: case ICMP_INFOREPLY: case ICMP_TRACEROUTE: ICMP_TRACE("ICMP message type %d not handled", icmp->type); break; default: ICMP_TRACE("ICMP message type %d unknown", icmp->type); break; } netFreebuf(pkt); return 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 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; }
/** * Fragments packet into maximum transmission unit sized chunks. * @param pkt the packet to fragment * @return OK */ syscall ipv4SendFrag(struct packet *pkt, struct netaddr *nxthop) { uint ihl; uchar *data; uint dRem = 0; // The amount of data remaining to be fragmented ushort froff; ushort lastFlag; ushort dLen; // Incoming packet structures struct ipv4Pkt *ip; // Outgoing packet structures struct ipv4Pkt *outip; struct packet *outpkt; IPv4_TRACE("Declaration"); if (NULL == pkt) { return SYSERR; } // Setup incoming packet structures ip = (struct ipv4Pkt *)pkt->curr; if (net2hs(ip->len) <= pkt->nif->mtu) { IPv4_TRACE("NetSend"); if (netaddrequal(&pkt->nif->ipbrc, nxthop)) { IPv4_TRACE("Subnet Broadcast"); return netSend(pkt, &NETADDR_GLOBAL_ETH_BRC, nxthop, ETHER_TYPE_IPv4); } return netSend(pkt, NULL, nxthop, ETHER_TYPE_IPv4); } // Verify header does not have DF if (net2hs(ip->flags_froff) & IPv4_FLAG_DF) { IPv4_TRACE("net2hs of froff"); /* Send ICMP message */ icmpDestUnreach(pkt, ICMP_FOFF_DFSET); return SYSERR; } ihl = (ip->ver_ihl & IPv4_IHL) * 4; data = ((uchar *)ip) + ihl; dRem = net2hs(ip->len) - ihl; froff = net2hs(ip->flags_froff) & IPv4_FROFF; lastFlag = net2hs(ip->flags_froff) & IPv4_FLAGS; // Length of data in this packet will be MTU - header length, // rounded down to nearest multiple of 8 bytes. dLen = (pkt->nif->mtu - ihl) & ~0x7; pkt->len = ihl + dLen; ip->len = hs2net(pkt->len); // Set more fragments flag ip->flags_froff = IPv4_FLAG_MF & froff; ip->flags_froff = hs2net(ip->flags_froff); ip->chksum = 0; ip->chksum = netChksum((uchar *)ip, ihl); netSend(pkt, NULL, nxthop, ETHER_TYPE_IPv4); dRem -= dLen; data += dLen; froff += (dLen / 8); // Get memory from stack for outgoing fragment outpkt = netGetbuf(); if (SYSERR == (int)outpkt) { IPv4_TRACE("allocating outpkt"); return SYSERR; } // Set up outgoing packet pointers and variables outpkt->curr -= pkt->nif->mtu; outip = (struct ipv4Pkt *)outpkt->curr; outpkt->nif = pkt->nif; memcpy(outip, ip, IPv4_HDR_LEN); // While packet must be fragmented while (dRem > 0) { if (((dRem + 7) & ~0x7) > pkt->nif->mtu + IPv4_HDR_LEN) { dLen = (pkt->nif->mtu - IPv4_HDR_LEN) & ~0x7; } else { dLen = dRem; } // Copy data segment memcpy(outip->opts, data, dLen); // Set more fragments flag if (dLen == dRem) { outip->flags_froff = lastFlag & froff; } else { outip->flags_froff = IPv4_FLAG_MF & froff; } outip->flags_froff = hs2net(outip->flags_froff); // Update fields outip->len = hs2net(IPv4_HDR_LEN + dLen); outip->chksum = 0; outip->chksum = netChksum((uchar *)outip, IPv4_HDR_LEN); // Update outgoing packet length outpkt->len = net2hs(outip->len); // Send fragment netSend(outpkt, NULL, nxthop, ETHER_TYPE_IPv4); dRem -= dLen; data += dLen; froff += (dLen / 8); } IPv4_TRACE("freeing outpkt"); netFreebuf(outpkt); return OK; }
/** * @ingroup snoop * * Print contents of an TCP packet * @param tcp TCP packet * @return OK if print successful, SYSERR if error occurs */ int snoopPrintTcp(struct tcpPkt *tcp, char verbose) { char descrp[40]; char output[40]; /* Error check pointer */ if (NULL == tcp) { return SYSERR; } if (verbose >= SNOOP_VERBOSE_ONE) { printf(" ----- TCP Header -----\n", ""); /* Source Port */ if (verbose >= SNOOP_VERBOSE_TWO) { snoopPrintTcpPort(net2hs(tcp->srcpt), descrp); } else { sprintf(descrp, ""); } sprintf(output, "%d %s", net2hs(tcp->srcpt), descrp); printf(" Src Port: %-25s ", output); /* Destination Port */ if (verbose >= SNOOP_VERBOSE_TWO) { snoopPrintTcpPort(net2hs(tcp->dstpt), descrp); } else { sprintf(descrp, ""); } sprintf(output, "%d %s", net2hs(tcp->dstpt), descrp); printf("Dst Port: %-25s\n", output); /* Sequence number */ printf(" Seq Num: %-14u ", net2hl(tcp->seqnum)); /* Acknowledgement number */ printf("Ack Num: %-14u ", net2hl(tcp->acknum)); /* Control flags */ printf("Ctrl: "); if (tcp->control & TCP_CTRL_URG) { printf("U "); } if (tcp->control & TCP_CTRL_ACK) { printf("A "); } if (tcp->control & TCP_CTRL_PSH) { printf("P "); } if (tcp->control & TCP_CTRL_RST) { printf("R "); } if (tcp->control & TCP_CTRL_SYN) { printf("S "); } if (tcp->control & TCP_CTRL_FIN) { printf("F "); } printf("\n"); if (verbose >= SNOOP_VERBOSE_TWO) { sprintf(output, "%d bytes", offset2octets(tcp->offset)); printf(" Offset: %-15s ", output); sprintf(output, "%d bytes", net2hs(tcp->window)); printf("Window: %-15s ", output); printf("Chksum: 0x%04X\n", net2hs(tcp->chksum)); } } return OK; }
/*------------------------------------------------------------------------ * x_rls - (command rls) list contents of remote file system directory *------------------------------------------------------------------------ */ COMMAND x_rls(int stdin, int stdout, int stderr, int nargs, char *args[]) { char *p, *buf; int dev, len; char str[256]; struct dirent { /* UNIX directory entry */ long d_inum; /* file's inode number */ short d_rlen; /* length of this record */ short d_nlen; /* length of this file's name */ char d_nam[1]; /* start of file name */ }; struct dirent *d; Bool aflag; aflag = FALSE; if (nargs > 1 && strcmp(p=args[1],"-a") == 0) { nargs--; aflag = TRUE; p = args[2]; } if (nargs == 1) p = "."; else if (nargs != 2) { printf("Usage: rls [-a] directory\n"); return(SYSERR); } if ( ((long)(buf=(char *)getmem(512))) == SYSERR) { fprintf(stderr, "rls: no memory\n"); return(SYSERR); } if (nammap(p, buf) != RFILSYS || (dev=open(NAMESPACE, p, "ro")) == SYSERR) { fprintf(stderr, "cannot open %s\n", p); freemem(buf, 512); return(SYSERR); } while ( (len = read(dev, buf, 512)) > 0) { for (p=buf ; p< &buf[512] ; p += d->d_rlen) { d = (struct dirent *)p; /* this could be a Vax or a Sun, so be */ /* prepared to swap integer fields */ if (d->d_nlen != strlen(d->d_nam)) { d->d_nlen = net2hs(d->d_nlen); d->d_rlen = net2hs(d->d_rlen); } if (d->d_inum == 0) continue; if (len < 512 || d->d_nlen != strlen(d->d_nam) || d->d_nlen > 255 || d->d_rlen < sizeof(struct dirent) || d->d_rlen > &buf[512] - p) { fprintf(stderr, "Not a directory\n"); close(dev); freemem(buf, 512L); return(SYSERR); } if (aflag || d->d_nam[0] != '.') { strcpy(str, d->d_nam); strcat(str, "\n"); write(stdout, str, strlen(str)); } if (d->d_rlen == 0) break; } } freemem(buf, 512); close(dev); return(OK); }
/** * Process an incoming IPv4 packet. * @param pkt pointer to the incoming packet * @return OK if packet was processed succesfully, otherwise SYSERR */ syscall ipv4Recv(struct packet *pkt) { struct ipv4Pkt *ip; struct netaddr dst; struct netaddr src; ushort iplen; /* Error check pointers */ if (NULL == pkt) { return SYSERR; } /* Setup pointer to IPv4 header */ pkt->nethdr = pkt->curr; ip = (struct ipv4Pkt *)pkt->curr; /* Verify the IP packet is valid */ if (FALSE == ipv4RecvValid(ip)) { IPv4_TRACE("Invalid packet"); netFreebuf(pkt); return SYSERR; } /* Obtain destination and source IP addresses */ dst.type = NETADDR_IPv4; dst.len = IPv4_ADDR_LEN; memcpy(dst.addr, ip->dst, dst.len); src.type = NETADDR_IPv4; src.len = IPv4_ADDR_LEN; memcpy(src.addr, ip->src, src.len); /* If packet is not destined for one of our network interfaces, * then attempt to route the packet */ if (FALSE == ipv4RecvDemux(&dst)) { IPv4_TRACE("Packet sent to routing subsystem"); #if NETEMU /* Run the packet through the network emulator if enabled */ return netemu(pkt); #else return rtRecv(pkt); #endif } /* Check if packet is fragmented */ if ((IPv4_FLAG_MF & net2hs(ip->flags_froff)) || (0 != (net2hs(ip->flags_froff) & IPv4_FROFF))) { // TODO: Handle fragmenting of packet // TODO: Should send icmp time exceeded // return ipRecvSendFrag(pkt, &dst); IPv4_TRACE("Packet fragmented"); netFreebuf(pkt); return SYSERR; } /* The Ethernet driver pads packets less than 60 bytes in length. * If the packet length returned from the Ethernet driver (pkt->len) * does not agree with the packet headers, adjust the packet length * to remove padding. */ iplen = net2hs(ip->len); if ((pkt->len - pkt->nif->linkhdrlen) > iplen) { pkt->len = pkt->nif->linkhdrlen + iplen; } /* Move current pointer to application level header */ pkt->curr += ((ip->ver_ihl & IPv4_IHL) << 2); IPv4_TRACE("IPv4 proto %d", ip->proto); /* Switch on packet protocol */ switch (ip->proto) { /* ICMP Packet */ case IPv4_PROTO_ICMP: icmpRecv(pkt); break; /* UDP Packet */ case IPv4_PROTO_UDP: #if NUDP udpRecv(pkt, &src, &dst); #endif /* NUDP */ break; /* TCP Packet */ case IPv4_PROTO_TCP: #if NTCP tcpRecv(pkt, &src, &dst); #endif /* NTCP */ break; /* Unknown IP packet protocol */ default: #if NRAW rawRecv(pkt, &src, &dst, ip->proto); #endif /* NRAW */ break; } return OK; }
/*------------------------------------------------------------------------ * gname - use the DNS to look up the name *------------------------------------------------------------------------ */ static IPaddr gname(char *nam) { IPaddr ip; char tmpstr[64]; /* temporary string buffer */ char *buf; /* buffer to hold domain query */ int dg, i; register char *p, *p2, *p3; register struct dn_mesg *dnptr; dnptr = (struct dn_mesg *) (buf = (char *) getmem(DN_MLEN)); dnptr->dn_id = 0; dnptr->dn_opparm = hs2net(DN_RD); dnptr->dn_qcount = hs2net(1); dnptr->dn_acount = dnptr->dn_ncount = dnptr->dn_dcount = 0; p = dnptr->dn_qaaa; strcpy(tmpstr, nam); p2 = tmpstr; while (p3=index(p2, '.')) { *p3 = '\0'; dn_cat(p, p2); p2 = p3+1; } dn_cat(p, p2); *p++ = NULLCH; /* terminate name */ /* Add query type and query class fields to name */ ( (struct dn_qsuf *)p )->dn_type = hs2net(DN_QTHA); ( (struct dn_qsuf *)p )->dn_clas = hs2net(DN_QCIN); p += sizeof(struct dn_qsuf); /* send query */ dg = open(UDP, NSERVER, ANYLPORT); control(dg, DG_SETMODE, DG_DMODE | DG_TMODE); write (dg, buf, p - buf); if ( (i = read(dg, buf, DN_MLEN)) == SYSERR || i == TIMEOUT) { close(dg); freemem(buf, DN_MLEN); return (IPaddr)SYSERR; } close(dg); if (net2hs(dnptr->dn_opparm) & DN_RESP || net2hs(dnptr->dn_acount) <= 0) { freemem(buf, DN_MLEN); return (IPaddr)SYSERR; } /* In answer, skip name and remainder of resource record header */ while (*p != NULLCH) if (*p & DN_CMPRS) /* compressed section of name */ *++p = NULLCH; else p += *p + 1; p += DN_RLEN + 1; /* Copy IP to user */ for (i=0; i < IP_ALEN; ++i) ((char *)&ip)[i] = *p++; freemem(buf, DN_MLEN); return ip; }