/* NOTE: icmp dont drop @ipkb */ void icmp_send(unsigned char type, unsigned char code, unsigned int data, struct pkbuf *pkb_in) { struct pkbuf *pkb; struct ip *iphdr = pkb2ip(pkb_in); struct icmp *icmphdr; int paylen = _ntohs(iphdr->ip_len); /* icmp payload length */ if (paylen < iphlen(iphdr) + 8) return; /* * RFC 1812 Section 4.3.2.7 for sanity check * An ICMP error message MUST NOT be sent as the result of receiving: * 1. A packet sent as a Link Layer broadcast or multicast * 2. A packet destined to an IP broadcast or IP multicast address *[3] A packet whose source address has a network prefix of zero or is an * invalid source address (as defined in Section [5.3.7]) * 4. Any fragment of a datagram other then the first fragment (i.e., a * packet for which the fragment offset in the IP header is nonzero). * 5. An ICMP error message */ if (pkb_in->pk_type != PKT_LOCALHOST) return; if (MULTICAST(iphdr->ip_dst) || BROADCAST(iphdr->ip_dst)) return; if (iphdr->ip_fragoff & _htons(IP_FRAG_OFF)) return; if (icmp_type_error(type) && iphdr->ip_pro == IP_P_ICMP) { icmphdr = ip2icmp(iphdr); if (icmphdr->icmp_type > ICMP_T_MAXNUM || icmp_error(icmphdr)) return; } /* build icmp packet and send */ /* ip packet size must be smaller than 576 bytes */ if (IP_HRD_SZ + ICMP_HRD_SZ + paylen > 576) paylen = 576 - IP_HRD_SZ - ICMP_HRD_SZ; pkb = alloc_pkb(ETH_HRD_SZ + IP_HRD_SZ + ICMP_HRD_SZ + paylen); icmphdr = (struct icmp *)(pkb2ip(pkb)->ip_data); icmphdr->icmp_type = type; icmphdr->icmp_code = code; icmphdr->icmp_cksum = 0; icmphdr->icmp_undata = data; memcpy(icmphdr->icmp_data, (unsigned char *)iphdr, paylen); icmphdr->icmp_cksum = icmp_chksum((unsigned short *)icmphdr, ICMP_HRD_SZ + paylen); icmpdbg("to "IPFMT"(payload %d) [type %d code %d]\n", ipfmt(iphdr->ip_src), paylen, type, code); ip_send_info(pkb, 0, IP_HRD_SZ + ICMP_HRD_SZ + paylen, 0, IP_P_ICMP, iphdr->ip_src); }
/* * Reset algorithm is not stated directly in RFC 793, * but we can conclude it according to all reset generation. * NOTE: maybe @tsk is NULL */ void tcp_send_reset(struct tcp_sock *tsk, struct tcp_segment *seg) { struct tcp *otcp, *tcphdr = seg->tcphdr; struct pkbuf *opkb; if (tcphdr->rst) return; opkb = alloc_pkb(ETH_HRD_SZ + IP_HRD_SZ + TCP_HRD_SZ); /* fill tcp head */ otcp = (struct tcp *)pkb2ip(opkb)->ip_data; otcp->src = tcphdr->dst; otcp->dst = tcphdr->src; if (tcphdr->ack) { /* * Should we set ack? * -Yes for xinu, it always set ack to seq+len * +No for Linux * +No for tapip */ otcp->seq = tcphdr->ackn; } else { otcp->ackn = _htonl(seg->seq + seg->len); otcp->ack = 1; } otcp->doff = TCP_HRD_DOFF; otcp->rst = 1; tcpdbg("send RESET from "IPFMT":%d to "IPFMT":%d", ipfmt(seg->iphdr->ip_dst), _ntohs(otcp->src), ipfmt(seg->iphdr->ip_src), _ntohs(otcp->dst)); tcp_send_out(NULL, opkb, seg); }
static int tcp_init_pkb(struct tcp_sock *tsk, struct pkbuf *pkb, unsigned int saddr, unsigned int daddr) { struct ip *iphdr = pkb2ip(pkb); /* fill ip head */ iphdr->ip_hlen = IP_HRD_SZ >> 2; iphdr->ip_ver = IP_VERSION_4; iphdr->ip_tos = 0; iphdr->ip_len = _htons(pkb->pk_len - ETH_HRD_SZ); iphdr->ip_id = _htons(tcp_id); iphdr->ip_fragoff = 0; iphdr->ip_ttl = TCP_DEFAULT_TTL; iphdr->ip_pro = IP_P_TCP; iphdr->ip_dst = daddr; /* NOTE: tsk maybe NULL, if connect doesnt exist */ if (tsk && tsk->sk.sk_dst) { pkb->pk_rtdst = tsk->sk.sk_dst; } else { if (rt_output(pkb) < 0) return -1; if (tsk) tsk->sk.sk_dst = pkb->pk_rtdst; } iphdr->ip_src = saddr; return 0; }
void tcp_send_fin(struct tcp_sock *tsk) { struct tcp *otcp; struct pkbuf *opkb; opkb = alloc_pkb(ETH_HRD_SZ + IP_HRD_SZ + TCP_HRD_SZ); /* fill tcp head */ otcp = (struct tcp *)pkb2ip(opkb)->ip_data; otcp->src = tsk->sk.sk_sport; otcp->dst = tsk->sk.sk_dport; otcp->doff = TCP_HRD_DOFF; otcp->seq = _htonl(tsk->snd_nxt); otcp->window = _htons(tsk->rcv_wnd); otcp->fin = 1; /* * Should we send an ACK? * Yes, tcp stack will drop packet if it has no ACK bit * according to RFC 793 #SEGMENT RECEIVE */ otcp->ackn = _htonl(tsk->rcv_nxt); otcp->ack = 1; tcpdbg("send FIN(%u)/ACK(%u) [WIN %d] to "IPFMT":%d", _ntohl(otcp->seq), _ntohl(otcp->ackn), _ntohs(otcp->window), ipfmt(tsk->sk.sk_daddr), _ntohs(otcp->dst)); tcp_send_out(tsk, opkb, NULL); }
void tcp_send_synack(struct tcp_sock *tsk, struct tcp_segment *seg) { /* * LISTEN : * SYN-SENT: * SEG: SYN, no ACK, no RST * <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK> * (ISS == SND.NXT) */ struct tcp *otcp, *tcphdr = seg->tcphdr; struct pkbuf *opkb; if (tcphdr->rst) return; opkb = alloc_pkb(ETH_HRD_SZ + IP_HRD_SZ + TCP_HRD_SZ); /* fill tcp head */ otcp = (struct tcp *)pkb2ip(opkb)->ip_data; otcp->src = tcphdr->dst; otcp->dst = tcphdr->src; otcp->doff = TCP_HRD_DOFF; otcp->seq = _htonl(tsk->iss); otcp->ackn = _htonl(tsk->rcv_nxt); otcp->syn = 1; otcp->ack = 1; otcp->window = _htons(tsk->rcv_wnd); tcpdbg("send SYN(%u)/ACK(%u) [WIN %d] to "IPFMT":%d", _ntohl(otcp->seq), _ntohs(otcp->window), _ntohl(otcp->ackn), ipfmt(seg->iphdr->ip_dst), _ntohs(otcp->dst)); tcp_send_out(tsk, opkb, seg); }
static int udp_init_pkb(struct sock *sk, struct pkbuf *pkb, void *buf, int size, struct sock_addr *skaddr) { struct ip *iphdr = pkb2ip(pkb); struct udp *udphdr = (struct udp *)iphdr->ip_data; /* fill ip head */ iphdr->ip_hlen = IP_HRD_SZ >> 2; iphdr->ip_ver = IP_VERSION_4; iphdr->ip_tos = 0; iphdr->ip_len = _htons(pkb->pk_len - ETH_HRD_SZ); iphdr->ip_id = _htons(udp_id); iphdr->ip_fragoff = 0; iphdr->ip_ttl = UDP_DEFAULT_TTL; iphdr->ip_pro = sk->protocol; /* IP_P_UDP */ iphdr->ip_dst = skaddr->dst_addr; /* FIXME:use the sk->rt_dst */ if (rt_output(pkb) < 0) /* fill ip src */ return -1; /* fill udp */ udphdr->src = sk->sk_sport; /* bound local address */ udphdr->dst = skaddr->dst_port; udphdr->length = _htons(size + UDP_HRD_SZ); memcpy(udphdr->data, buf, size); udpdbg(IPFMT":%d" "->" IPFMT":%d(proto %d)", ipfmt(iphdr->ip_src), _ntohs(udphdr->src), ipfmt(iphdr->ip_dst), _ntohs(udphdr->dst), iphdr->ip_pro); udp_set_checksum(iphdr, udphdr); return 0; }
void icmp_in(struct pkbuf *pkb) { struct ip *iphdr = pkb2ip(pkb); struct icmp *icmphdr = ip2icmp(iphdr); int icmplen, type; /* sanity check */ icmplen = ipdlen(iphdr); icmpdbg("%d bytes", icmplen); if (icmplen < ICMP_HRD_SZ) { icmpdbg("icmp header is too small"); goto drop_pkb; } if (icmp_chksum((unsigned short *)icmphdr, icmplen) != 0) { icmpdbg("icmp checksum is error"); goto drop_pkb; } type = icmphdr->icmp_type; if (type > ICMP_T_MAXNUM) { icmpdbg("unknown icmp type %d code %d", type, icmphdr->icmp_code); goto drop_pkb; } /* handle icmp type */ icmp_table[type].handler(&icmp_table[type], pkb); return; drop_pkb: free_pkb(pkb); }
static void icmp_echo_request(struct icmp_desc *icmp_desc, struct pkbuf *pkb) { struct ip *iphdr = pkb2ip(pkb); struct icmp *icmphdr = ip2icmp(iphdr); icmpdbg("echo request data %d bytes icmp_id %d icmp_seq %d", (int)(iphdr->ip_len - iphlen(iphdr) - ICMP_HRD_SZ), _ntohs(icmphdr->icmp_id), _ntohs(icmphdr->icmp_seq)); if (icmphdr->icmp_code) { icmpdbg("echo request packet corrupted"); free_pkb(pkb); return; } icmphdr->icmp_type = ICMP_T_ECHORLY; /* * adjacent the checksum: * If ~ >>> (cksum + x + 8) >>> == 0 * let ~ >>> (cksum` + x ) >>> == 0 * then cksum` = cksum + 8 */ if (icmphdr->icmp_cksum >= 0xffff - ICMP_T_ECHOREQ) icmphdr->icmp_cksum += ICMP_T_ECHOREQ + 1; else icmphdr->icmp_cksum += ICMP_T_ECHOREQ; iphdr->ip_dst = iphdr->ip_src; /* ip_src is set by ip_send_out() */ ip_hton(iphdr); /* init reused input packet */ pkb->pk_rtdst = NULL; pkb->pk_indev = NULL; pkb->pk_type = PKT_NONE; ip_send_out(pkb); }
static void icmp_drop_reply(struct icmp_desc *icmp_desc, struct pkbuf *pkb) { struct ip *iphdr = pkb2ip(pkb); struct icmp *icmphdr = ip2icmp(iphdr); icmpdbg("icmp type %d code %d (dropped)", icmphdr->icmp_type, icmphdr->icmp_code); if (icmp_desc->info) icmpdbg("%s", icmp_desc->info); free_pkb(pkb); }
static void icmp_echo_reply(struct icmp_desc *icmp_desc, struct pkbuf *pkb) { struct ip *iphdr = pkb2ip(pkb); struct icmp *icmphdr = ip2icmp(iphdr); icmpdbg("from "IPFMT" id %d seq %d ttl %d", ipfmt(iphdr->ip_src), _ntohs(icmphdr->icmp_id), _ntohs(icmphdr->icmp_seq), iphdr->ip_ttl); free_pkb(pkb); }
static void icmp_redirect(struct icmp_desc *icmp_desc, struct pkbuf *pkb) { struct ip *iphdr = pkb2ip(pkb); struct icmp *icmphdr = ip2icmp(iphdr); if (icmphdr->icmp_code > 4) icmpdbg("Redirect code %d is error", icmphdr->icmp_code); else icmpdbg("from " IPFMT " %s(new nexthop "IPFMT")", ipfmt(iphdr->ip_src), redirectstr[icmphdr->icmp_code], ipfmt(icmphdr->icmp_gw)); free_pkb(pkb); }
void tcp_send_out(struct tcp_sock *tsk, struct pkbuf *pkb, struct tcp_segment *seg) { struct ip *iphdr = pkb2ip(pkb); struct tcp *tcphdr = (struct tcp *)iphdr->ip_data; unsigned int saddr, daddr; if (seg) { daddr = seg->iphdr->ip_src; saddr = seg->iphdr->ip_dst; } else if (tsk) { daddr = tsk->sk.sk_daddr; saddr = tsk->sk.sk_saddr; } else /* This shouldnt happen. */ assert(0); if (tcp_init_pkb(tsk, pkb, saddr, daddr) < 0) { free_pkb(pkb); return; } tcp_set_checksum(iphdr, tcphdr); ip_send_out(pkb); }
void tcp_send_syn(struct tcp_sock *tsk, struct tcp_segment *seg) { /* * SYN-SENT: */ struct tcp *otcp; struct pkbuf *opkb; opkb = alloc_pkb(ETH_HRD_SZ + IP_HRD_SZ + TCP_HRD_SZ); /* fill tcp head */ otcp = (struct tcp *)pkb2ip(opkb)->ip_data; otcp->src = tsk->sk.sk_sport; otcp->dst = tsk->sk.sk_dport; otcp->doff = TCP_HRD_DOFF; otcp->seq = _htonl(tsk->iss); otcp->syn = 1; otcp->window = _htons(tsk->rcv_wnd); tcpdbg("send SYN(%u) [WIN %d] to "IPFMT":%d", _ntohl(otcp->seq), _ntohs(otcp->window), ipfmt(tsk->sk.sk_daddr), _ntohs(otcp->dst)); tcp_send_out(tsk, opkb, seg); }
static void recv_packet(void) { struct pkbuf *pkb; struct ip *iphdr; struct icmp *icmphdr; while (!finite || recv > 0) { pkb = _recv(sock); if (!pkb) break; iphdr = pkb2ip(pkb); icmphdr = ip2icmp(iphdr); if (iphdr->ip_pro == IP_P_ICMP && _ntohs(icmphdr->icmp_id) == id && icmphdr->icmp_type == ICMP_T_ECHORLY) { recv--; printf("%d bytes from " IPFMT ": icmp_seq=%d ttl=%d\n", ipdlen(iphdr), ipfmt(iphdr->ip_src), _ntohs(icmphdr->icmp_seq), iphdr->ip_ttl); precv++; } free_pkb(pkb); } }
/* * Acknowledgment algorithm is not stated directly in RFC 793, * but we can conclude it from all acknowledgment situation. */ void tcp_send_ack(struct tcp_sock *tsk, struct tcp_segment *seg) { /* * SYN-SENT : * SEG: SYN, acceptable ACK, no RST (SND.NXT = SEG.SEQ+1) * <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> * SYN-RECEIVED / ESTABLISHED / FIN-WAIT-1 / FIN-WAIT-2 / * CLOSE-WAIT / CLOSING / LAST-ACK / TIME-WAIT : * SEG: no RST, ??ACK, ??SYN (segment is not acceptable) * <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> * ESTABLISHED / FIN-WAIT-1 / FIN-WAIT-2 / process the segment text: * SEG: ACK, no RST * <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> * (This acknowledgment should be piggybacked on a segment being * transmitted if possible without incurring undue delay.) */ struct tcp *otcp, *tcphdr = seg->tcphdr; struct pkbuf *opkb; if (tcphdr->rst) return; opkb = alloc_pkb(ETH_HRD_SZ + IP_HRD_SZ + TCP_HRD_SZ); /* fill tcp head */ otcp = (struct tcp *)pkb2ip(opkb)->ip_data; otcp->src = tcphdr->dst; otcp->dst = tcphdr->src; otcp->doff = TCP_HRD_DOFF; otcp->seq = _htonl(tsk->snd_nxt); otcp->ackn = _htonl(tsk->rcv_nxt); otcp->ack = 1; otcp->window = _htons(tsk->rcv_wnd); tcpdbg("send ACK(%u) [WIN %d] to "IPFMT":%d", _ntohl(otcp->ackn), _ntohs(otcp->window), ipfmt(seg->iphdr->ip_src), _ntohs(otcp->dst)); tcp_send_out(tsk, opkb, seg); }