/* * bittwiste - pcap capture file editor * Copyright (C) 2007 Addy Yeow Chin Heng * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "bittwiste.h" char *program_name; /* general options */ int header_opt = 0; /* specifies which header to edit */ int layer_opt = 0; /* copy up to the specified layer only */ int start_opt = 0; /* copy the specified range of packets only */ int end_opt = 0; int start_oset_opt = 0; /* delete the specified byte offset */ int end_oset_opt = 0; time_t start_sec_opt = 0; /* copy packets within the specified timeframe only */ time_t end_sec_opt = 0; int csum_opt = 1; /* set to 0 to disable checksum correction */ u_char *payload_opt = NULL; /* payload in hex digits *NOTFREED* */ u_short payload_len_opt = 0; /* length of payload in bytes */ int linktype_opt = -1; /* pcap preamble link type field, -1 -> no override */ /* header specific options *NOTFREED* */ struct ethopt *ethopt; /* Ethernet options */ struct arpopt *arpopt; /* ARP options */ struct ipopt *ipopt; /* IP options */ struct icmpopt *icmpopt; /* ICMP options */ struct tcpopt *tcpopt; /* TCP options */ struct udpopt *udpopt; /* UDP options */ /* stats */ static u_int pkts = 0; static u_int bytes = 0; int main(int argc, char **argv) { char *cp; int c, i; char *str = NULL; char *infile = NULL; char *outfile = NULL; struct tm *tm = NULL; if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; /* process general options */ while ((c = getopt(argc, argv, "I:O:L:X:CM:D:R:S:T:h")) != -1) { switch (c) { case 'I': infile = optarg; break; case 'O': outfile = optarg; break; case 'L': layer_opt = strtol(optarg, NULL, 0); /* * 2 - Ethernet * 3 - ARP, IP * 4 - ICMP, TCP, UDP */ if (layer_opt < 2 || layer_opt > 4) error("layer is out of range"); break; case 'X': /* ignored if option -L and -T are not specified */ c = strlen(optarg); if (c > (PAYLOAD_MAX * 2) || (c % 2) != 0) error("invalid payload specification"); for (i = 0; i < c; i++) { if (!isxdigit(optarg[i])) error("invalid payload specification"); } payload_len_opt = (u_short)c / 2; /* possible resizing in editing functions */ payload_opt = (u_char *)malloc(sizeof(u_char) * payload_len_opt); if (payload_opt == NULL) error("malloc(): cannot allocate memory for payload_opt"); /* make a byte of data from every 2 characters of optarg */ for (i = 0; i < payload_len_opt; i++) { /* ugly - let me know if there is a better way to achieve this */ sscanf(optarg, "%02x", &payload_opt[i]); *optarg++; *optarg++; /* move pass 2 characters */ } break; case 'C': csum_opt = 0; /* DISABLE checksum correction */ break; case 'M': linktype_opt = strtol(optarg, NULL, 0); /* * 1 - Ethernet * 9 - PPP * 12 - Raw IP * 51 - PPPoE * 105 - IEEE 802.11 wireless * 117 - OpenBSD pflog * 118 - Cisco IOS * 119 - 802.11 with Prism header */ if (linktype_opt < 0 || linktype_opt > UCHAR_MAX) error("linktype is out of range"); break; case 'D': /* -D 15-18, delete from byte 15th through byte 18th (inclusive), starting from link-layer header */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, "-")) == NULL) error("invalid offset specification"); start_oset_opt = strtol(cp, NULL, 0); if ((cp = (char *)strtok(NULL, "-")) == NULL) end_oset_opt = start_oset_opt; /* delete a single byte, e.g. -D 15 */ else end_oset_opt = strtol(cp, NULL, 0); free(str); str = NULL; if (start_oset_opt == 0 || end_oset_opt == 0 || (start_oset_opt > end_oset_opt)) error("invalid offset specification"); break; case 'R': /* range: 5-21 */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, "-")) == NULL) error("invalid range specification"); start_opt = strtol(cp, NULL, 0); if ((cp = (char *)strtok(NULL, "-")) == NULL) end_opt = start_opt; /* only one packet */ else end_opt = strtol(cp, NULL, 0); free(str); str = NULL; if (start_opt == 0 || end_opt == 0 || (start_opt > end_opt)) error("invalid range specification"); break; case 'S': /* * time frame with one-second resolution: -S 22/10/2006,21:47:35-24/10/2006,13:16:05 * format: -S DD/MM/YYYY,HH:MM:SS-DD/MM/YYYY,HH:MM:SS * note that -S 22/10/2006-24/10/2006 is equivalent to -S 22/10/2006,00:00:00-24/10/2006,00:00:00 */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, "-")) == NULL) error("invalid timeframe specification"); tm = (struct tm *)malloc(sizeof(struct tm)); if (tm == NULL) error("malloc(): cannot allocate memory for tm"); if (!strptime(cp, "%d/%m/%Y,%T", tm)) error("invalid timeframe specification"); start_sec_opt = mktime(tm); if ((cp = (char *)strtok(NULL, "-")) == NULL) end_sec_opt = start_sec_opt; /* only the packets within the one-second resolution */ else { if (!strptime(cp, "%d/%m/%Y,%T", tm)) error("invalid timeframe specification"); } end_sec_opt = mktime(tm); free(tm); tm = NULL; free(str); str = NULL; if (start_sec_opt > end_sec_opt) error("invalid timeframe specification"); break; case 'T': if (strcasecmp(optarg, "eth") == 0) header_opt = ETH; else if (strcasecmp(optarg, "arp") == 0) header_opt = ARP; else if (strcasecmp(optarg, "ip") == 0) header_opt = IP; else if (strcasecmp(optarg, "icmp") == 0) header_opt = ICMP; else if (strcasecmp(optarg, "tcp") == 0) header_opt = TCP; else if (strcasecmp(optarg, "udp") == 0) header_opt = UDP; else error("invalid header specification"); /* process header specific options */ parse_header_options(argc, argv); break; case 'h': default: usage(); } } if (infile == NULL) error("input file not specified"); if (outfile == NULL) error("output file not specified"); if (strcmp(infile, outfile) == 0) error("invalid outfile specification"); parse_trace(infile, outfile); info(); exit(EXIT_SUCCESS); } void parse_header_options(int argc, char **argv) { char *cp; int c; double d; /* validate and store TCP sequence and acknowledgment number */ struct ether_addr *ether_addr; struct in_addr in_addr; char *str = NULL; if (header_opt == ETH) { ethopt = (struct ethopt *)malloc(sizeof(struct ethopt)); if (ethopt == NULL) error("malloc(): cannot allocate memory for ethopt"); memset(ethopt, 0, sizeof(struct ethopt)); while ((c = getopt(argc, argv, "d:s:t:")) != -1) { switch (c) { case 'd': /* destination MAC */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid destination MAC address"); if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid destination MAC address"); memcpy(ethopt->ether_old_dhost, ether_addr, sizeof(struct ether_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all destination MAC */ ethopt->ether_dhost_flag = 1; else { /* overwrite matching destination MAC only */ ethopt->ether_dhost_flag = 2; if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid destination MAC address"); memcpy(ethopt->ether_new_dhost, ether_addr, sizeof(struct ether_addr)); } free(str); str = NULL; break; case 's': /* source MAC */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid source MAC address"); if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid source MAC address"); memcpy(ethopt->ether_old_shost, ether_addr, sizeof(struct ether_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all source MAC */ ethopt->ether_shost_flag = 1; else { /* overwrite matching source MAC only */ ethopt->ether_shost_flag = 2; if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid source MAC address"); memcpy(ethopt->ether_new_shost, ether_addr, sizeof(struct ether_addr)); } free(str); str = NULL; break; case 't': /* type */ if (strcasecmp(optarg, "ip") == 0) ethopt->ether_type = ETHERTYPE_IP; else if (strcasecmp(optarg, "arp") == 0) ethopt->ether_type = ETHERTYPE_ARP; else error("invalid Ethernet type specification"); break; default: usage(); } } } else if (header_opt == ARP) { arpopt = (struct arpopt *)malloc(sizeof(struct arpopt)); if (arpopt == NULL) error("malloc(): cannot allocate memory for arpopt"); memset(arpopt, 0, sizeof(struct arpopt)); while ((c = getopt(argc, argv, "o:s:p:t:q:")) != -1) { switch (c) { case 'o': /* opcode */ c = strtol(optarg, NULL, 0); if (c < 0 || c > USHRT_MAX) error("ARP opcode is out of range"); arpopt->ar_op = (u_short)c; arpopt->ar_op_flag = 1; break; case 's': /* sender MAC */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid sender MAC address"); if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid sender MAC address"); memcpy(arpopt->ar_old_sha, ether_addr, sizeof(struct ether_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all sender MAC */ arpopt->ar_sha_flag = 1; else { /* overwrite matching sender MAC only */ arpopt->ar_sha_flag = 2; if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid sender MAC address"); memcpy(arpopt->ar_new_sha, ether_addr, sizeof(struct ether_addr)); } free(str); str = NULL; break; case 'p': /* sender IP */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid sender IP address"); if (inet_aton(cp, &in_addr) == 0) error("invalid sender IP address"); memcpy(&arpopt->ar_old_spa, &in_addr, sizeof(struct in_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all sender IP address */ arpopt->ar_spa_flag = 1; else { /* overwrite matching IP address only */ arpopt->ar_spa_flag = 2; if (inet_aton(cp, &in_addr) == 0) error("invalid sender IP address"); memcpy(&arpopt->ar_new_spa, &in_addr, sizeof(struct in_addr)); } free(str); str = NULL; break; case 't': /* target MAC */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid target MAC address"); if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid target MAC address"); memcpy(arpopt->ar_old_tha, ether_addr, sizeof(struct ether_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all target MAC */ arpopt->ar_tha_flag = 1; else { /* overwrite matching target MAC only */ arpopt->ar_tha_flag = 2; if ((ether_addr = (struct ether_addr *)ether_aton(cp)) == NULL) error("invalid target MAC address"); memcpy(arpopt->ar_new_tha, ether_addr, sizeof(struct ether_addr)); } free(str); str = NULL; break; case 'q': /* target IP */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid target IP address"); if (inet_aton(cp, &in_addr) == 0) error("invalid target IP address"); memcpy(&arpopt->ar_old_tpa, &in_addr, sizeof(struct in_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all target IP address */ arpopt->ar_tpa_flag = 1; else { /* overwrite matching IP address only */ arpopt->ar_tpa_flag = 2; if (inet_aton(cp, &in_addr) == 0) error("invalid target IP address"); memcpy(&arpopt->ar_new_tpa, &in_addr, sizeof(struct in_addr)); } free(str); str = NULL; break; default: usage(); } } } else if (header_opt == IP) { ipopt = (struct ipopt *)malloc(sizeof(struct ipopt)); if (ipopt == NULL) error("malloc(): cannot allocate memory for ipopt"); memset(ipopt, 0, sizeof(struct ipopt)); while ((c = getopt(argc, argv, "i:f:o:t:p:s:d:")) != -1) { switch (c) { case 'i': /* identification */ c = strtol(optarg, NULL, 0); if (c < 0 || c > USHRT_MAX) error("IP identification is out of range"); ipopt->ip_id = (u_short)c; ipopt->ip_id_flag = 1; break; case 'f': /* flags */ for (c = 0; optarg[c]; c++) optarg[c] = tolower(optarg[c]); if (strchr(optarg, 'r') != NULL) /* reserved bit */ ipopt->ip_flag_r = 1; if (strchr(optarg, 'd') != NULL) /* don't fragment bit */ ipopt->ip_flag_d = 1; if (strchr(optarg, 'm') != NULL) /* more fragment bit */ ipopt->ip_flag_m = 1; if (strchr(optarg, '-') != NULL) { /* remove flags */ ipopt->ip_flag_r = 0; ipopt->ip_flag_d = 0; ipopt->ip_flag_m = 0; } ipopt->ip_flags_flag = 1; break; case 'o': /* fragment offset */ c = strtol(optarg, NULL, 0); if (c < 0 || c > IP_FO_MAX) error("IP fragment offset is out of range"); ipopt->ip_fo = (u_short)c; ipopt->ip_fo_flag = 1; break; case 't': /* time to live */ c = strtol(optarg, NULL, 0); if (c < 0 || c > UCHAR_MAX) error("IP time to live is out of range"); ipopt->ip_ttl = (u_char)c; ipopt->ip_ttl_flag = 1; break; case 'p': /* protocol */ c = strtol(optarg, NULL, 0); if (c < 0 || c > UCHAR_MAX) error("IP protocol is out of range"); ipopt->ip_p = (u_char)c; ipopt->ip_p_flag = 1; break; case 's': /* source IP */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid source IP address"); if (inet_aton(cp, &in_addr) == 0) error("invalid source IP address"); memcpy(&ipopt->ip_old_src, &in_addr, sizeof(struct in_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all source IP address */ ipopt->ip_src_flag = 1; else { /* overwrite matching IP address only */ ipopt->ip_src_flag = 2; if (inet_aton(cp, &in_addr) == 0) error("invalid source IP address"); memcpy(&ipopt->ip_new_src, &in_addr, sizeof(struct in_addr)); } free(str); str = NULL; break; case 'd': /* destination IP */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid destination IP address"); if (inet_aton(cp, &in_addr) == 0) error("invalid destination IP address"); memcpy(&ipopt->ip_old_dst, &in_addr, sizeof(struct in_addr)); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all destination IP address */ ipopt->ip_dst_flag = 1; else { /* overwrite matching IP address only */ ipopt->ip_dst_flag = 2; if (inet_aton(cp, &in_addr) == 0) error("invalid destination IP address"); memcpy(&ipopt->ip_new_dst, &in_addr, sizeof(struct in_addr)); } free(str); str = NULL; break; default: usage(); } } } else if (header_opt == ICMP) { icmpopt = (struct icmpopt *)malloc(sizeof(struct icmpopt)); if (icmpopt == NULL) error("malloc(): cannot allocate memory for icmpopt"); memset(icmpopt, 0, sizeof(struct icmpopt)); while ((c = getopt(argc, argv, "t:c:")) != -1) { switch (c) { case 't': /* type */ c = strtol(optarg, NULL, 0); if (c < 0 || c > UCHAR_MAX) error("ICMP type is out of range"); icmpopt->icmp_type = (u_char)c; icmpopt->icmp_type_flag = 1; break; case 'c': /* code */ c = strtol(optarg, NULL, 0); if (c < 0 || c > UCHAR_MAX) error("ICMP code is out of range"); icmpopt->icmp_code = (u_char)c; icmpopt->icmp_code_flag = 1; break; default: usage(); } } } else if (header_opt == TCP) { tcpopt = (struct tcpopt *)malloc(sizeof(struct tcpopt)); if (tcpopt == NULL) error("malloc(): cannot allocate memory for tcpopt"); memset(tcpopt, 0, sizeof(struct tcpopt)); while ((c = getopt(argc, argv, "s:d:q:a:f:w:u:")) != -1) { switch (c) { case 's': /* source port */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid TCP source port specification"); c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("TCP source port is out of range"); tcpopt->th_old_sport = (u_short)c; if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all source port */ tcpopt->th_sport_flag = 1; else { /* overwrite matching port only */ c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("TCP source port is out of range"); tcpopt->th_new_sport = (u_short)c; tcpopt->th_sport_flag = 2; } free(str); str = NULL; break; case 'd': /* destination port */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid TCP destination port specification"); c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("TCP destination port is out of range"); tcpopt->th_old_dport = (u_short)c; if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all destination port */ tcpopt->th_dport_flag = 1; else { /* overwrite matching port only */ c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("TCP destination port is out of range"); tcpopt->th_new_dport = (u_short)c; tcpopt->th_dport_flag = 2; } free(str); str = NULL; break; case 'q': /* sequence number */ d = strtod(optarg, NULL); if (d < 0 || d > UINT_MAX) error("TCP sequence number is out of range"); tcpopt->th_seq = (tcp_seq)d; tcpopt->th_seq_flag = 1; break; case 'a': /* acknowledgment number */ d = strtod(optarg, NULL); if (d < 0 || d > UINT_MAX) error("TCP acknowledgment number is out of range"); tcpopt->th_ack = (tcp_seq)d; tcpopt->th_ack_flag = 1; break; case 'f': /* flags */ for (c = 0; optarg[c]; c++) optarg[c] = tolower(optarg[c]); if (strchr(optarg, 'u') != NULL) /* URG */ tcpopt->th_flag_u = 1; if (strchr(optarg, 'a') != NULL) /* ACK */ tcpopt->th_flag_a = 1; if (strchr(optarg, 'p') != NULL) /* PSH */ tcpopt->th_flag_p = 1; if (strchr(optarg, 'r') != NULL) /* RST */ tcpopt->th_flag_r = 1; if (strchr(optarg, 's') != NULL) /* SYN */ tcpopt->th_flag_s = 1; if (strchr(optarg, 'f') != NULL) /* FIN */ tcpopt->th_flag_f = 1; if (strchr(optarg, '-') != NULL) { /* remove flags */ tcpopt->th_flag_u = 0; tcpopt->th_flag_a = 0; tcpopt->th_flag_p = 0; tcpopt->th_flag_r = 0; tcpopt->th_flag_s = 0; tcpopt->th_flag_f = 0; } tcpopt->th_flags_flag = 1; break; case 'w': /* window size */ c = strtol(optarg, NULL, 0); if (c < 0 || c > USHRT_MAX) error("TCP window size is out of range"); tcpopt->th_win = (u_short)c; tcpopt->th_win_flag = 1; break; case 'u': /* urgent pointer */ c = strtol(optarg, NULL, 0); if (c < 0 || c > USHRT_MAX) error("TCP urgent pointer is out of range"); tcpopt->th_urp = (u_short)c; tcpopt->th_urp_flag = 1; break; default: usage(); } } } else if (header_opt == UDP) { udpopt = (struct udpopt *)malloc(sizeof(struct udpopt)); if (udpopt == NULL) error("malloc(): cannot allocate memory for udpopt"); memset(udpopt, 0, sizeof(struct udpopt)); while ((c = getopt(argc, argv, "s:d:")) != -1) { switch (c) { case 's': /* source port */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid UDP source port specification"); c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("UDP source port is out of range"); udpopt->uh_old_sport = (u_short)c; if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all source port */ udpopt->uh_sport_flag = 1; else { /* overwrite matching port only */ c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("UDP source port is out of range"); udpopt->uh_new_sport = (u_short)c; udpopt->uh_sport_flag = 2; } free(str); str = NULL; break; case 'd': /* destination port */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid UDP destination port specification"); c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("UDP destination port is out of range"); udpopt->uh_old_dport = (u_short)c; if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all destination port */ udpopt->uh_dport_flag = 1; else { /* overwrite matching port only */ c = strtol(cp, NULL, 0); if (c < 0 || c > USHRT_MAX) error("UDP destination port is out of range"); udpopt->uh_new_dport = (u_short)c; udpopt->uh_dport_flag = 2; } free(str); str = NULL; break; default: usage(); } } } /* NOTREACHED */ } void parse_trace(char *infile, char *outfile) { FILE *fp; /* file pointer to trace file */ FILE *fp_outfile; /* file pointer to modified trace file */ struct pcap_file_header preamble; struct pcap_pkthdr *header; u_char *pkt_data; /* original packet data starting from link-layer header */ u_char *new_pkt_data; /* modified pkt_data inclusive of pcap generic header is written here */ int ret; int i; int pkt_index; /* to check if we are within start_opt and end_opt for range specification */ int len; /* original header->caplen */ int end_o; /* aligned end_oset_opt */ notice("input file: %s", infile); if ((fp = fopen(infile, "rb")) == NULL) error("fopen(): error reading %s", infile); notice("output file: %s", outfile); if ((fp_outfile = fopen(outfile, "wb")) == NULL) error("fopen(): error creating %s", outfile); /* preamble occupies the first 24 bytes of a trace file */ if (fread(&preamble, sizeof(preamble), 1, fp) == 0) error("fread(): error reading %s", infile); if (preamble.magic != PCAP_MAGIC) error("%s is not a valid pcap based trace file", infile); /* override pcap preamble link type with user specified link type */ if (linktype_opt >= 0) preamble.linktype = linktype_opt; /* write preamble to modified trace file */ if (fwrite(&preamble, sizeof(preamble), 1, fp_outfile) != 1) error("fwrite(): error writing %s", outfile); /* pcap generic header */ header = (struct pcap_pkthdr *)malloc(PCAP_HDR_LEN); if (header == NULL) error("malloc(): cannot allocate memory for header"); /* * loop through the remaining data by reading the pcap generic header first. * pcap generic header (16 bytes) = secs. + usecs. + caplen + len */ pkt_index = 1; while ((ret = fread(header, PCAP_HDR_LEN, 1, fp))) { if (ret == 0) error("fread(): error reading %s", infile); /* original packet data starting from link-layer header */ pkt_data = (u_char *)malloc(sizeof(u_char) * header->caplen); if (pkt_data == NULL) error("malloc(): cannot allocate memory for pkt_data"); memset(pkt_data, 0, header->caplen); /* copy captured packet data starting from link-layer header into pkt_data */ if (fread(pkt_data, header->caplen, 1, fp) == 0) error("fread(): error reading %s", infile); if ((pkt_index >= start_opt && pkt_index <= end_opt) || (start_opt == 0 && end_opt == 0)) { if ((header->ts.tv_sec >= start_sec_opt && header->ts.tv_sec <= end_sec_opt) || (start_sec_opt == 0 && end_sec_opt == 0)) { /* byte deletion mode, no content modification (parse_ethernet(), etc.) */ if (start_oset_opt != 0 && end_oset_opt != 0 && start_oset_opt <= header->caplen) { /* align end_oset_opt so that it does not go beyond header->caplen */ if (end_oset_opt > header->caplen) end_o = header->caplen; else end_o = end_oset_opt; len = header->caplen; /* original capture length (before byte deletion) */ header->caplen = header->len = len - ((end_o - start_oset_opt) + 1); /* write pcap generic header */ if (fwrite(header, PCAP_HDR_LEN, 1, fp_outfile) != 1) error("fwrite(): error writing %s", outfile); for (i = 0; i < start_oset_opt - 1; i++) { if (fputc(pkt_data[i], fp_outfile) == EOF) error("fputc(): error writing %s", outfile); } for (i = end_o; i < len; i++) { if (fputc(pkt_data[i], fp_outfile) == EOF) error("fputc(): error writing %s", outfile); } } else { /* modified pkt_data inclusive of pcap generic header */ new_pkt_data = (u_char *)malloc(sizeof(u_char) * (PCAP_HDR_LEN + ETHER_MAX_LEN)); /* 16 + 1514 bytes */ if (new_pkt_data == NULL) error("malloc(): cannot allocate memory for new_pkt_data"); memset(new_pkt_data, 0, PCAP_HDR_LEN + ETHER_MAX_LEN); /* * encapsulated editing function starting from link-layer header. * ret = bytes written in new_pkt_data */ ret = parse_ethernet(pkt_data, new_pkt_data, header) + PCAP_HDR_LEN; /* copy pcap generic header into new_pkt_data */ memcpy(new_pkt_data, header, PCAP_HDR_LEN); /* no changes */ if (ret == PCAP_HDR_LEN) { /* parse_ethernet() returns 0 */ /* write pcap generic header */ if (fwrite(header, PCAP_HDR_LEN, 1, fp_outfile) != 1) error("fwrite(): error writing %s", outfile); if (fwrite(pkt_data, header->caplen, 1, fp_outfile) != 1) error("fwrite(): error writing %s", outfile); } /* overwrite the entire pkt_data with new_pkt_data */ else if (ret == header->caplen + PCAP_HDR_LEN) { if (fwrite(new_pkt_data, ret, 1, fp_outfile) != 1) error("fwrite(): error writing %s", outfile); } else { if (fwrite(new_pkt_data, ret, 1, fp_outfile) != 1) error("fwrite(): error writing %s", outfile); /* write remaining bytes from pkt_data */ for (i = ret - PCAP_HDR_LEN; i < header->caplen; i++) { if (fputc(pkt_data[i], fp_outfile) == EOF) error("fputc(): error writing %s", outfile); } } free(new_pkt_data); new_pkt_data = NULL; } ++pkts; /* packets written */ bytes += header->caplen; /* bytes written */ } } free(pkt_data); pkt_data = NULL; ++pkt_index; } /* end while */ free(header); header = NULL; (void)fclose(fp); (void)fclose(fp_outfile); } u_short parse_ethernet(const u_char *pkt_data, u_char *new_pkt_data, struct pcap_pkthdr *header) { /* * Ethernet header (14 bytes) * 1. destination MAC (6 bytes) * 2. source MAC (6 bytes) * 3. type (2 bytes) */ struct ether_header *eth_hdr; u_short ether_type; int i; /* do nothing if Ethernet header is truncated */ if (header->caplen < ETHER_HDR_LEN) return (0); eth_hdr = (struct ether_header *)malloc(ETHER_HDR_LEN); if (eth_hdr == NULL) error("malloc(): cannot allocate memory for eth_hdr"); /* copy Ethernet header from pkt_data into eth_hdr */ memcpy(eth_hdr, pkt_data, ETHER_HDR_LEN); /* we are editing Ethernet header */ if (header_opt == ETH) { /* overwrite destination MAC */ if (ethopt->ether_dhost_flag == 1) /* overwrite all destination MAC */ memcpy(eth_hdr->ether_dhost, ethopt->ether_old_dhost, ETHER_ADDR_LEN); else if (ethopt->ether_dhost_flag == 2 && /* overwrite matching destination MAC only */ memcmp(eth_hdr->ether_dhost, ethopt->ether_old_dhost, ETHER_ADDR_LEN) == 0) memcpy(eth_hdr->ether_dhost, ethopt->ether_new_dhost, ETHER_ADDR_LEN); /* overwrite source MAC */ if (ethopt->ether_shost_flag == 1) /* overwrite all source MAC */ memcpy(eth_hdr->ether_shost, ethopt->ether_old_shost, ETHER_ADDR_LEN); else if (ethopt->ether_shost_flag == 2 && /* overwrite matching source MAC only */ memcmp(eth_hdr->ether_shost, ethopt->ether_old_shost, ETHER_ADDR_LEN) == 0) memcpy(eth_hdr->ether_shost, ethopt->ether_new_shost, ETHER_ADDR_LEN); /* overwrite Ethernet type */ if (ethopt->ether_type != 0) eth_hdr->ether_type = htons(ethopt->ether_type); } ether_type = ntohs(eth_hdr->ether_type); /* * go pass pcap generic header in new_pkt_data then copy eth_hdr into * new_pkt_data and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, eth_hdr, ETHER_HDR_LEN); free(eth_hdr); eth_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN) *new_pkt_data--; /* copy up to layer 2 only, discard remaining data */ if (layer_opt == 2) { /* we are editing Ethernet header and we have payload */ if (header_opt == ETH && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETHER_HDR_LEN) > ETHER_MAX_LEN) payload_len_opt -= (payload_len_opt + ETHER_HDR_LEN) - ETHER_MAX_LEN; /* * go pass pcap generic header and Ethernet header in new_pkt_data * then copy payload_opt into new_pkt_data and reset pointer to the * beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN) *new_pkt_data--; header->caplen = header->len = ETHER_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETHER_HDR_LEN; return (header->caplen); } /* parse ARP datagram */ if (ether_type == ETHERTYPE_ARP) return (parse_arp(pkt_data, new_pkt_data, header)); /* parse IP datagram */ else if (ether_type == ETHERTYPE_IP) return (parse_ip(pkt_data, new_pkt_data, header, NULL, 0)); /* no further editing support for other datagram */ else return (ETHER_HDR_LEN); } u_short parse_arp(const u_char *pkt_data, u_char *new_pkt_data, struct pcap_pkthdr *header) { /* * Ethernet ARP header (28 bytes) * 1. hardware type (2 bytes) * 2. protocol type (2 bytes) * 3. hardware address length (1 byte) * 4. protocol address length (1 byte) * 5. opcode (2 bytes) * 6. sender hardware address (6 bytes) * 7. sender protocol address (4 bytes) * 8. target hardware address (6 bytes) * 9. target protocol address (4 bytes) */ struct arphdr *arp_hdr; int i; /* do nothing if ARP header is truncated */ if (header->caplen < ETHER_HDR_LEN + ARP_HDR_LEN) return (ETHER_HDR_LEN); /* go pass Ethernet header in pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data++; arp_hdr = (struct arphdr *)malloc(ARP_HDR_LEN); if (arp_hdr == NULL) error("malloc(): cannot allocate memory for arp_hdr"); /* copy ARP header from pkt_data into arp_hdr */ memcpy(arp_hdr, pkt_data, ARP_HDR_LEN); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data--; /* do nothing if this is an unsupported ARP header */ if (arp_hdr->ar_hln != ETHER_ADDR_LEN || arp_hdr->ar_pln != IP_ADDR_LEN) { free(arp_hdr); arp_hdr = NULL; return (ETHER_HDR_LEN); } /* we are editing ARP header */ if (header_opt == ARP) { /* overwrite opcode */ if (arpopt->ar_op_flag) arp_hdr->ar_op = htons(arpopt->ar_op); /* overwrite sender MAC */ if (arpopt->ar_sha_flag == 1) /* overwrite all sender MAC */ memcpy(arp_hdr->ar_sha, arpopt->ar_old_sha, ETHER_ADDR_LEN); else if (arpopt->ar_sha_flag == 2 && /* overwrite matching sender MAC only */ memcmp(arp_hdr->ar_sha, arpopt->ar_old_sha, ETHER_ADDR_LEN) == 0) memcpy(arp_hdr->ar_sha, arpopt->ar_new_sha, ETHER_ADDR_LEN); /* overwrite sender IP */ if (arpopt->ar_spa_flag == 1) /* overwrite all sender IP */ memcpy(arp_hdr->ar_spa, arpopt->ar_old_spa, IP_ADDR_LEN); else if (arpopt->ar_spa_flag == 2 && /* overwrite matching IP only */ memcmp(arp_hdr->ar_spa, arpopt->ar_old_spa, IP_ADDR_LEN) == 0) memcpy(arp_hdr->ar_spa, arpopt->ar_new_spa, IP_ADDR_LEN); /* overwrite target MAC */ if (arpopt->ar_tha_flag == 1) /* overwrite all target MAC */ memcpy(arp_hdr->ar_tha, arpopt->ar_old_tha, ETHER_ADDR_LEN); else if (arpopt->ar_tha_flag == 2 && /* overwrite matching target MAC only */ memcmp(arp_hdr->ar_tha, arpopt->ar_old_tha, ETHER_ADDR_LEN) == 0) memcpy(arp_hdr->ar_tha, arpopt->ar_new_tha, ETHER_ADDR_LEN); /* overwrite target IP */ if (arpopt->ar_tpa_flag == 1) /* overwrite all target IP */ memcpy(arp_hdr->ar_tpa, arpopt->ar_old_tpa, IP_ADDR_LEN); else if (arpopt->ar_tpa_flag == 2 && /* overwrite matching IP only */ memcmp(arp_hdr->ar_tpa, arpopt->ar_old_tpa, IP_ADDR_LEN) == 0) memcpy(arp_hdr->ar_tpa, arpopt->ar_new_tpa, IP_ADDR_LEN); } /* * go pass pcap generic header and Ethernet header in new_pkt_data * then copy arp_hdr into new_pkt_data and reset pointer to the * beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, arp_hdr, ARP_HDR_LEN); free(arp_hdr); arp_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN) *new_pkt_data--; /* copy up to layer 3 only, discard remaining data */ if (layer_opt == 3) { /* we are editing ARP header and we have payload */ if (header_opt == ARP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETHER_HDR_LEN + ARP_HDR_LEN) > ETHER_MAX_LEN) payload_len_opt -= (payload_len_opt + ETHER_HDR_LEN + ARP_HDR_LEN) - ETHER_MAX_LEN; /* * go pass pcap generic header, Ethernet header and ARP header in * new_pkt_data then copy payload_opt into new_pkt_data and reset * pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ARP_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ARP_HDR_LEN) *new_pkt_data--; header->caplen = header->len = ETHER_HDR_LEN + ARP_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETHER_HDR_LEN + ARP_HDR_LEN; return (header->caplen); } /* no further editing support after ARP header */ return (ETHER_HDR_LEN + ARP_HDR_LEN); } u_short parse_ip(const u_char *pkt_data, u_char *new_pkt_data, struct pcap_pkthdr *header, struct ip *ip_hdr, int flag) { /* * IP header (20 bytes + optional X bytes for options) * 1. version (4 bits) * 2. header length (4 bits) * 3. service type (1 byte) * 4. total length (2 bytes) * 5. id (2 bytes) * 6. flag (3 bits) * 7. fragment offset (13 bits) * 8. ttl (1 byte) * 9. protocol (1 byte) * 10. header checksum (2 bytes) * 11. source IP (4 bytes) * 12. destination IP (4 bytes) * 13. options (X bytes) */ u_char *ip_hdr_o; /* IP header with options (for header checksum calculation) */ u_short ip_hlb; /* header length in bytes */ u_short ip_fo; /* fragment offset (number of 64-bit segments) */ u_char r = '\0'; /* flags */ u_char d = '\0'; u_char m = '\0'; u_char ip_p = '\0'; /* protocol */ u_char *ip_o = NULL; /* options (X bytes) */ int i, j; /* * flag is 0; entry from Ethernet header to edit IP header. * flag is 1; entry from ICMP, TCP or UDP header to update IP total length * and recalculate checksum for IP header. */ if (flag == 0) { /* do nothing if IP header is truncated */ if (header->caplen < ETHER_HDR_LEN + IP_HDR_LEN) return (ETHER_HDR_LEN); /* go pass Ethernet header in pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data++; ip_hdr = (struct ip *)malloc(IP_HDR_LEN); if (ip_hdr == NULL) error("malloc(): cannot allocate memory for ip_hdr"); /* copy IP header from pkt_data into ip_hdr */ memcpy(ip_hdr, pkt_data, IP_HDR_LEN); } ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* have IP options */ if (ip_hlb > IP_HDR_LEN) { /* do nothing if IP header with options is truncated */ if (header->caplen < ETHER_HDR_LEN + ip_hlb) { /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data--; free(ip_hdr); ip_hdr = NULL; return (ETHER_HDR_LEN); } ip_o = (u_char *)malloc(sizeof(u_char) * (ip_hlb - IP_HDR_LEN)); if (ip_o == NULL) error("malloc(): cannot allocate memory for ip_o"); /* copy IP options into ip_o */ for (i = 0, j = IP_HDR_LEN; i < (ip_hlb - IP_HDR_LEN); i++, j++) ip_o[i] = pkt_data[j]; } if (flag == 0) { /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data--; /* we are editing IP header */ if (header_opt == IP) { /* overwrite identification */ if (ipopt->ip_id_flag) ip_hdr->ip_id = htons(ipopt->ip_id); /* original fragment offset */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* original flags */ r = (ntohs(ip_hdr->ip_off) & IP_RF) > 0 ? 1 : 0; d = (ntohs(ip_hdr->ip_off) & IP_DF) > 0 ? 1 : 0; m = (ntohs(ip_hdr->ip_off) & IP_MF) > 0 ? 1 : 0; /* overwrite fragment offset only */ if (ipopt->ip_fo_flag & !ipopt->ip_flags_flag) { ip_hdr->ip_off = htons((ipopt->ip_fo & IP_OFFMASK) | (r ? IP_RF : 0) | (d ? IP_DF : 0) | (m ? IP_MF : 0)); } /* overwrite flags only */ else if (!ipopt->ip_fo_flag & ipopt->ip_flags_flag) { ip_hdr->ip_off = htons((ip_fo & IP_OFFMASK) | ((ipopt->ip_flag_r) ? IP_RF : 0) | ((ipopt->ip_flag_d) ? IP_DF : 0) | ((ipopt->ip_flag_m) ? IP_MF : 0)); } /* overwrite fragment offset and flags */ else if (ipopt->ip_fo_flag & ipopt->ip_flags_flag) { ip_hdr->ip_off = htons((ipopt->ip_fo & IP_OFFMASK) | ((ipopt->ip_flag_r) ? IP_RF : 0) | ((ipopt->ip_flag_d) ? IP_DF : 0) | ((ipopt->ip_flag_m) ? IP_MF : 0)); } /* overwrite time to live */ if (ipopt->ip_ttl_flag) ip_hdr->ip_ttl = ipopt->ip_ttl; /* overwrite protocol */ if (ipopt->ip_p_flag) ip_hdr->ip_p = ipopt->ip_p; /* overwrite source IP */ if (ipopt->ip_src_flag == 1) /* overwrite all source IP */ memcpy(&ip_hdr->ip_src, &ipopt->ip_old_src, sizeof(struct in_addr)); else if (ipopt->ip_src_flag == 2 && /* overwrite matching IP only */ memcmp(&ip_hdr->ip_src, &ipopt->ip_old_src, sizeof(struct in_addr)) == 0) memcpy(&ip_hdr->ip_src, &ipopt->ip_new_src, sizeof(struct in_addr)); /* overwrite destination IP */ if (ipopt->ip_dst_flag == 1) /* overwrite all destination IP */ memcpy(&ip_hdr->ip_dst, &ipopt->ip_old_dst, sizeof(struct in_addr)); else if (ipopt->ip_dst_flag == 2 && /* overwrite matching IP only */ memcmp(&ip_hdr->ip_dst, &ipopt->ip_old_dst, sizeof(struct in_addr)) == 0) memcpy(&ip_hdr->ip_dst, &ipopt->ip_new_dst, sizeof(struct in_addr)); } /* * if more fragment flag is set, we should not parse the protocol header * (ICMP, TCP, or UDP) just yet since this is a fragmented packet */ m = (ntohs(ip_hdr->ip_off) & IP_MF) > 0 ? 1 : 0; ip_p = ip_hdr->ip_p; /* we are going to copy up to layer 3 only, change total length */ if (layer_opt == 3) { /* we are editing IP header and we have payload, include its length in total length */ if (header_opt == IP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETHER_HDR_LEN + ip_hlb) > ETHER_MAX_LEN) payload_len_opt -= (payload_len_opt + ETHER_HDR_LEN + ip_hlb) - ETHER_MAX_LEN; ip_hdr->ip_len = htons(ip_hlb + payload_len_opt); } else ip_hdr->ip_len = htons(ip_hlb); } } /* recalculate checksum (cover IP header only) */ if (csum_opt) { ip_hdr->ip_sum = 0x0000; /* clear checksum field */ /* have IP options */ if (ip_hlb > IP_HDR_LEN) { ip_hdr_o = (u_char *)malloc(sizeof(u_char) * ip_hlb); if (ip_hdr_o == NULL) error("malloc(): cannot allocate memory for ip_hdr_o"); /* * copy ip_hdr into ip_hdr_o, go pass IP header in ip_hdr_o then * copy ip_o into ip_hdr_o and reset pointer to the beginning of * ip_hdr_o and finally calculate checksum of ip_hdr_o */ memcpy(ip_hdr_o, ip_hdr, IP_HDR_LEN); i = 0; while (i++ < IP_HDR_LEN) *ip_hdr_o++; memcpy(ip_hdr_o, ip_o, ip_hlb - IP_HDR_LEN); i = 0; while (i++ < IP_HDR_LEN) *ip_hdr_o--; ip_hdr->ip_sum = htons(cksum(ip_hdr_o, ip_hlb)); free(ip_hdr_o); ip_hdr_o = NULL; } else ip_hdr->ip_sum = htons(cksum((u_char *)ip_hdr, ip_hlb)); } /* * go pass pcap generic header and Ethernet header in new_pkt_data * then copy ip_hdr and ip_o (if exist) into new_pkt_data and reset * pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, ip_hdr, IP_HDR_LEN); /* have IP options */ if (ip_hlb > IP_HDR_LEN) { i = 0; while (i++ < IP_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, ip_o, ip_hlb - IP_HDR_LEN); free(ip_o); ip_o = NULL; i = 0; while (i++ < IP_HDR_LEN) *new_pkt_data--; } i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN) *new_pkt_data--; if (flag == 0) { /* copy up to layer 3 only, discard remaining data */ if (layer_opt == 3) { /* we are editing IP header and we have payload */ if (header_opt == IP && payload_len_opt > 0) { /* * go pass pcap generic header, Ethernet header and IP header in * new_pkt_data then copy payload_opt into new_pkt_data and reset * pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data--; header->caplen = header->len = ETHER_HDR_LEN + ip_hlb + payload_len_opt; /* * if payload is specified and it applies to ICMP, TCP, or UDP header + data, * and checksum correction on this payload is needed, * and more fragment flag is not set -> not a fragmented packet */ if (csum_opt && !m) { /* parse ICMP datagram */ if (ip_p == IPPROTO_ICMP) return (parse_icmp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse TCP datagram */ else if (ip_p == IPPROTO_TCP) return (parse_tcp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse UDP datagram */ else if (ip_p == IPPROTO_UDP) return (parse_udp(pkt_data, new_pkt_data, header, ip_hdr)); } } else header->caplen = header->len = ETHER_HDR_LEN + ip_hlb; free(ip_hdr); ip_hdr = NULL; return (header->caplen); } /* !m means more fragment flag is not set -> not a fragmented packet */ if (!m) { /* parse ICMP datagram */ if (ip_p == IPPROTO_ICMP) return (parse_icmp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse TCP datagram */ else if (ip_p == IPPROTO_TCP) return (parse_tcp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse UDP datagram */ else if (ip_p == IPPROTO_UDP) return (parse_udp(pkt_data, new_pkt_data, header, ip_hdr)); } /* no further editing support for other datagram or fragmented packet */ free(ip_hdr); ip_hdr = NULL; return (ETHER_HDR_LEN + ip_hlb); } return (0); /* flag is 1 */ } u_short parse_icmp(const u_char *pkt_data, u_char *new_pkt_data, struct pcap_pkthdr *header, struct ip *ip_hdr) { /* * ICMP header (4 bytes) * 1. type (1 byte) * 2. code (1 byte) * 3. checksum (2 bytes) */ struct icmphdr *icmp_hdr; u_char *icmpp; /* ICMP header + trailing data */ u_short icmpp_len; u_short ip_hlb; /* IP header length in bytes */ u_short ip_fo; /* IP fragment offset (number of 64-bit segments) */ int i; ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* do nothing if ICMP header is truncated */ if (header->caplen < ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN) { free(ip_hdr); ip_hdr = NULL; return (ETHER_HDR_LEN + ip_hlb); } icmp_hdr = (struct icmphdr *)malloc(ICMP_HDR_LEN); if (icmp_hdr == NULL) error("malloc(): cannot allocate memory for icmp_hdr"); /* * we have payload which covers ICMP header + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* * go pass pcap generic header, Ethernet header and IP header * in new_pkt_data then copy ICMP header from new_pkt_data * into icmp_hdr and reset pointer to the beginning of * new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data++; memcpy(icmp_hdr, new_pkt_data, ICMP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data--; } else { /* * go pass Ethernet header and IP header in pkt_data * then copy ICMP header from pkt_data into icmp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETHER_HDR_LEN + ip_hlb)) *pkt_data++; memcpy(icmp_hdr, pkt_data, ICMP_HDR_LEN); i = 0; while (i++ < (ETHER_HDR_LEN + ip_hlb)) *pkt_data--; /* we are editing ICMP header */ if (header_opt == ICMP) { /* overwrite type */ if (icmpopt->icmp_type_flag) icmp_hdr->icmp_type = icmpopt->icmp_type; /* overwrite code */ if (icmpopt->icmp_code_flag) icmp_hdr->icmp_code = icmpopt->icmp_code; } /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing ICMP header and we have payload, attach * the payload first before checksum calculation. */ if (header_opt == ICMP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN) > ETHER_MAX_LEN) payload_len_opt -= (payload_len_opt + ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN) - ETHER_MAX_LEN; /* * go pass pcap generic header, Ethernet header, IP header * and ICMP header in new_pkt_data then copy payload_opt * into new_pkt_data and reset pointer to the beginning of * new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN) *new_pkt_data--; header->caplen = header->len = ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN; /* update IP total length */ ip_hdr->ip_len = htons(header->caplen - ETHER_HDR_LEN); /* go pass Ethernet header in pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data++; /* * reuse parsing function for IP header to update IP total length in * new_pkt_data and recalculate checksum for IP header if required. */ (void)parse_ip(pkt_data, new_pkt_data, header, ip_hdr, 1); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data--; } } /* we have no support for checksum calculation for fragmented packet */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* recalculate checksum for ICMP header (cover ICMP header + trailing data) */ if (csum_opt && ip_fo == 0) { /* recalculate checksum if we have enough data */ if (header->caplen >= (ETHER_HDR_LEN + ntohs(ip_hdr->ip_len))) { icmpp_len = ntohs(ip_hdr->ip_len) - ip_hlb; /* icmpp_len must be even for correct checksum calculation */ if ((icmpp_len % 2) != 0) icmpp = (u_char *)malloc(sizeof(u_char) * (icmpp_len + 1)); else icmpp = (u_char *)malloc(sizeof(u_char) * icmpp_len); if (icmpp == NULL) error("malloc(): cannot allocate memory for icmpp"); if ((icmpp_len % 2) != 0) memset(icmpp, 0, icmpp_len + 1); else memset(icmpp, 0, icmpp_len); /* clear checksum field */ icmp_hdr->icmp_cksum = 0x0000; /* copy ICMP header from icmp_hdr into icmpp */ memcpy(icmpp, icmp_hdr, ICMP_HDR_LEN); /* copy trailing data from payload_opt into icmpp */ if (layer_opt == 4 && header_opt == ICMP && payload_len_opt > 0) { for (i = ICMP_HDR_LEN; i < (ICMP_HDR_LEN + payload_len_opt); i++) icmpp[i] = payload_opt[i - ICMP_HDR_LEN]; } /* copy trailing data from payload_opt (payload after IP header) into icmpp */ else if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { for (i = ICMP_HDR_LEN; i < payload_len_opt; i++) icmpp[i] = payload_opt[i]; } /* copy trailing data from pkt_data into icmpp */ else { for (i = ICMP_HDR_LEN; i < icmpp_len; i++) icmpp[i] = pkt_data[ETHER_HDR_LEN + ip_hlb + i]; } /* recalculate checksum */ if ((icmpp_len % 2) != 0) icmp_hdr->icmp_cksum = cksum(icmpp, icmpp_len + 1); else icmp_hdr->icmp_cksum = cksum(icmpp, icmpp_len); icmp_hdr->icmp_cksum = htons(icmp_hdr->icmp_cksum); free(icmpp); icmpp = NULL; } } free(ip_hdr); ip_hdr = NULL; /* * go pass pcap generic header, Ethernet header and IP header * in new_pkt_data then copy icmp_hdr into new_pkt_data and * reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data++; memcpy(new_pkt_data, icmp_hdr, ICMP_HDR_LEN); free(icmp_hdr); icmp_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data--; /* no further editing support after ICMP header */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IP header) which covers ICMP header + data, * checksum for ICMP header corrected above, * while ICMP data is written to new_pkt_data in parse_ip() */ else if (layer_opt == 3) return (header->caplen); else return (ETHER_HDR_LEN + ip_hlb + ICMP_HDR_LEN); } u_short parse_tcp(const u_char *pkt_data, u_char *new_pkt_data, struct pcap_pkthdr *header, struct ip *ip_hdr) { /* * TCP header (20 bytes + optional X bytes for options) * 1. source port (2 bytes) * 2. destination port (2 bytes) * 3. sequence number (4 bytes) * 4. acknowledgment number (4 bytes) * 5. data offset (4 bits) - number of 32-bit segments in TCP header * 6. reserved (6 bits) * 7. flags (6 bits) * 8. window (2 bytes) * 9. checksum (2 bytes) * 10. urgent pointer (2 bytes) * 11. options (X bytes) */ struct tcphdr *tcp_hdr; u_char *tcp_o = NULL; /* options (X bytes) */ u_short tcp_hlb; /* TCP header length in bytes */ u_char *tcpp; /* IP pseudo header + TCP header (with options if exist) + trailing data */ u_short tcpp_len; struct ippseudo *ipp; /* IP pseudo header */ u_short ip_hlb; /* IP header length in bytes */ u_short ip_fo; /* IP fragment offset (number of 64-bit segments) */ int i, j; ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* do nothing if TCP header is truncated */ if (header->caplen < ETHER_HDR_LEN + ip_hlb + TCP_HDR_LEN) { free(ip_hdr); ip_hdr = NULL; return (ETHER_HDR_LEN + ip_hlb); } tcp_hdr = (struct tcphdr *)malloc(TCP_HDR_LEN); if (tcp_hdr == NULL) error("malloc(): cannot allocate memory for tcp_hdr"); /* * we have payload which covers TCP header + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* * go pass pcap generic header, Ethernet header and IP header * in new_pkt_data then copy TCP header from new_pkt_data * into tcp_hdr and reset pointer to the beginning of * new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data++; memcpy(tcp_hdr, new_pkt_data, TCP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data--; } else { /* * go pass Ethernet header and IP header in pkt_data * then copy TCP header from pkt_data into tcp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETHER_HDR_LEN + ip_hlb)) *pkt_data++; memcpy(tcp_hdr, pkt_data, TCP_HDR_LEN); i = 0; while (i++ < (ETHER_HDR_LEN + ip_hlb)) *pkt_data--; } tcp_hlb = tcp_hdr->th_off * 4; /* convert to bytes */ /* have TCP options */ if (tcp_hlb > TCP_HDR_LEN) { /* do nothing if TCP header with options is truncated */ if (header->caplen < (ETHER_HDR_LEN + ip_hlb + tcp_hlb)) { free(ip_hdr); ip_hdr = NULL; free(tcp_hdr); tcp_hdr = NULL; return (ETHER_HDR_LEN + ip_hlb); } tcp_o = (u_char *)malloc(sizeof(u_char) * (tcp_hlb - TCP_HDR_LEN)); if (tcp_o == NULL) error("malloc(): cannot allocate memory for tcp_o"); if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* copy TCP options from new_pkt_data into tcp_o */ for (i = 0, j = TCP_HDR_LEN; i < (tcp_hlb - TCP_HDR_LEN); i++, j++) tcp_o[i] = new_pkt_data[PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb + j]; } else { /* copy TCP options from pkt_data into tcp_o */ for (i = 0, j = TCP_HDR_LEN; i < (tcp_hlb - TCP_HDR_LEN); i++, j++) tcp_o[i] = pkt_data[ETHER_HDR_LEN + ip_hlb + j]; } } /* we are editing TCP header */ if (header_opt == TCP) { /* overwrite source port */ if (tcpopt->th_sport_flag == 1) /* overwrite all source port */ tcp_hdr->th_sport = htons(tcpopt->th_old_sport); else if (tcpopt->th_sport_flag == 2 && /* overwrite matching port only */ tcp_hdr->th_sport == htons(tcpopt->th_old_sport)) tcp_hdr->th_sport = htons(tcpopt->th_new_sport); /* overwrite destination port */ if (tcpopt->th_dport_flag == 1) /* overwrite all destination port */ tcp_hdr->th_dport = htons(tcpopt->th_old_dport); else if (tcpopt->th_dport_flag == 2 && /* overwrite matching port only */ tcp_hdr->th_dport == htons(tcpopt->th_old_dport)) tcp_hdr->th_dport = htons(tcpopt->th_new_dport); /* overwrite sequence number */ if (tcpopt->th_seq_flag) tcp_hdr->th_seq = htonl(tcpopt->th_seq); /* overwrite acknowledgment number */ if (tcpopt->th_ack_flag) tcp_hdr->th_ack = htonl(tcpopt->th_ack); /* overwrite flags */ if (tcpopt->th_flags_flag) tcp_hdr->th_flags = ((tcpopt->th_flag_u ? TH_URG : 0) | (tcpopt->th_flag_a ? TH_ACK : 0) | (tcpopt->th_flag_p ? TH_PUSH : 0) | (tcpopt->th_flag_r ? TH_RST : 0) | (tcpopt->th_flag_s ? TH_SYN : 0) | (tcpopt->th_flag_f ? TH_FIN : 0)); /* overwrite window size */ if (tcpopt->th_win_flag) tcp_hdr->th_win = htons(tcpopt->th_win); /* overwrite urgent pointer */ if (tcpopt->th_urp_flag) tcp_hdr->th_urp = htons(tcpopt->th_urp); } /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing TCP header and we have payload, attach * the payload first before checksum calculation */ if (header_opt == TCP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETHER_HDR_LEN + ip_hlb + tcp_hlb) > ETHER_MAX_LEN) payload_len_opt -= (payload_len_opt + ETHER_HDR_LEN + ip_hlb + tcp_hlb) - ETHER_MAX_LEN; /* * go pass pcap generic header, Ethernet header, IP header * and TCP header in new_pkt_data then copy payload_opt * into new_pkt_data and reset pointer to the beginning of * new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb + tcp_hlb) *new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb + tcp_hlb) *new_pkt_data--; header->caplen = header->len = ETHER_HDR_LEN + ip_hlb + tcp_hlb + payload_len_opt; } else header->caplen = header->len = ETHER_HDR_LEN + ip_hlb + tcp_hlb; /* update IP total length */ ip_hdr->ip_len = htons(header->caplen - ETHER_HDR_LEN); /* go pass Ethernet header in pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data++; /* * reuse parsing function for IP header to update IP total length in * new_pkt_data and recalculate checksum for IP header if required. */ (void)parse_ip(pkt_data, new_pkt_data, header, ip_hdr, 1); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data--; } /* we have no support for checksum calculation for fragmented packet */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* recalculate checksum for TCP header (cover IP pseudo header + TCP header + trailing data) */ if (csum_opt && ip_fo == 0) { /* recalculate checksum if we have enough data */ if (header->caplen >= (ETHER_HDR_LEN + ntohs(ip_hdr->ip_len))) { /* create IP pseudo header */ ipp = (struct ippseudo *)malloc(sizeof(struct ippseudo)); if (ipp == NULL) error("malloc(): cannot allocate memory for ipp"); memcpy(&ipp->ippseudo_src, &ip_hdr->ip_src, sizeof(struct in_addr)); memcpy(&ipp->ippseudo_dst, &ip_hdr->ip_dst, sizeof(struct in_addr)); ipp->ippseudo_pad = 0x00; ipp->ippseudo_p = ip_hdr->ip_p; ipp->ippseudo_len = htons(ntohs(ip_hdr->ip_len) - ip_hlb); tcpp_len = sizeof(struct ippseudo) + ntohs(ipp->ippseudo_len); /* tcpp_len must be even for correct checksum calculation */ if ((tcpp_len % 2) != 0) tcpp = (u_char *)malloc(sizeof(u_char) * (tcpp_len + 1)); else tcpp = (u_char *)malloc(sizeof(u_char) * tcpp_len); if (tcpp == NULL) error("malloc(): cannot allocate memory for tcpp"); if ((tcpp_len % 2) != 0) memset(tcpp, 0, tcpp_len + 1); else memset(tcpp, 0, tcpp_len); /* copy IP pseudo header from ipp into tcpp */ memcpy(tcpp, ipp, sizeof(struct ippseudo)); free(ipp); ipp = NULL; /* go pass IP pseudo header in tcpp */ i = 0; while (i++ < sizeof(struct ippseudo)) *tcpp++; /* clear checksum field */ tcp_hdr->th_sum = 0x0000; /* copy TCP header from tcp_hdr into tcpp */ memcpy(tcpp, tcp_hdr, TCP_HDR_LEN); /* * have TCP options, go pass TCP header in tcpp then copy tcp_o into tcpp * and reset pointer of tcpp to go pass IP pseudo header only */ if (tcp_hlb > TCP_HDR_LEN) { i = 0; while (i++ < TCP_HDR_LEN) *tcpp++; memcpy(tcpp, tcp_o, tcp_hlb - TCP_HDR_LEN); i = 0; while (i++ < TCP_HDR_LEN) *tcpp--; } /* reset pointer to the beginning of tcpp */ i = 0; while (i++ < sizeof(struct ippseudo)) *tcpp--; /* copy trailing data from payload_opt into tcpp */ if (layer_opt == 4 && header_opt == TCP && payload_len_opt > 0) { for (i = tcp_hlb; i < (tcpp_len - sizeof(struct ippseudo)); i++) tcpp[i + sizeof(struct ippseudo)] = payload_opt[i - tcp_hlb]; } /* copy trailing data from payload_opt (payload after IP header) into tcpp */ else if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { for (i = tcp_hlb; i < payload_len_opt; i++) tcpp[i + sizeof(struct ippseudo)] = payload_opt[i]; } /* copy trailing data from pkt_data into tcpp */ else { for (i = tcp_hlb; i < (tcpp_len - sizeof(struct ippseudo)); i++) tcpp[i + sizeof(struct ippseudo)] = pkt_data[ETHER_HDR_LEN + ip_hlb + i]; } /* recalculate checksum */ if ((tcpp_len % 2) != 0) tcp_hdr->th_sum = cksum(tcpp, tcpp_len + 1); else tcp_hdr->th_sum = cksum(tcpp, tcpp_len); tcp_hdr->th_sum = htons(tcp_hdr->th_sum); free(tcpp); tcpp = NULL; } } free(ip_hdr); ip_hdr = NULL; /* * go pass pcap generic header, Ethernet header and IP header * in new_pkt_data then copy tcp_hdr and tcp_o (if exist) into * new_pkt_data and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data++; memcpy(new_pkt_data, tcp_hdr, TCP_HDR_LEN); free(tcp_hdr); tcp_hdr = NULL; /* have TCP options */ if (tcp_hlb > TCP_HDR_LEN) { i = 0; while (i++ < TCP_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, tcp_o, tcp_hlb - TCP_HDR_LEN); free(tcp_o); tcp_o = NULL; i = 0; while (i++ < TCP_HDR_LEN) *new_pkt_data--; } i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data--; /* no further editing support after TCP header */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IP header) which covers TCP header + data, * checksum for TCP header corrected above, * while TCP data is written to new_pkt_data in parse_ip() */ else if (layer_opt == 3) return (header->caplen); else return (ETHER_HDR_LEN + ip_hlb + tcp_hlb); } u_short parse_udp(const u_char *pkt_data, u_char *new_pkt_data, struct pcap_pkthdr *header, struct ip *ip_hdr) { /* * UDP header (8 bytes) * 1. source port (2 bytes) * 2. destination port (2 bytes) * 3. length (2 bytes) * 4. checksum (2 bytes) */ struct udphdr *udp_hdr; u_char *udpp; /* IP pseudo header + UDP header + trailing data */ u_short udpp_len; struct ippseudo *ipp; /* IP pseudo header */ u_short ip_hlb; /* IP header length in bytes */ u_short ip_fo; /* IP fragment offset (number of 64-bit segments) */ int i; ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* do nothing if UDP header is truncated */ if (header->caplen < ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN) { free(ip_hdr); ip_hdr = NULL; return (ETHER_HDR_LEN + ip_hlb); } udp_hdr = (struct udphdr *)malloc(UDP_HDR_LEN); if (udp_hdr == NULL) error("malloc(): cannot allocate memory for udp_hdr"); /* * we have payload which covers UDP header + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* * go pass pcap generic header, Ethernet header and IP header * in new_pkt_data then copy UDP header from new_pkt_data * into udp_hdr and reset pointer to the beginning of * new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data++; memcpy(udp_hdr, new_pkt_data, UDP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data--; } else { /* * go pass Ethernet header and IP header in pkt_data * then copy UDP header from pkt_data into udp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETHER_HDR_LEN + ip_hlb)) *pkt_data++; memcpy(udp_hdr, pkt_data, UDP_HDR_LEN); i = 0; while (i++ < (ETHER_HDR_LEN + ip_hlb)) *pkt_data--; } /* we are editing UDP header */ if (header_opt == UDP) { /* overwrite source port */ if (udpopt->uh_sport_flag == 1) /* overwrite all source port */ udp_hdr->uh_sport = htons(udpopt->uh_old_sport); else if (udpopt->uh_sport_flag == 2 && /* overwrite matching port only */ udp_hdr->uh_sport == htons(udpopt->uh_old_sport)) udp_hdr->uh_sport = htons(udpopt->uh_new_sport); /* overwrite destination port */ if (udpopt->uh_dport_flag == 1) /* overwrite all destination port */ udp_hdr->uh_dport = htons(udpopt->uh_old_dport); else if (udpopt->uh_dport_flag == 2 && /* overwrite matching port only */ udp_hdr->uh_dport == htons(udpopt->uh_old_dport)) udp_hdr->uh_dport = htons(udpopt->uh_new_dport); } /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing UDP header and we have payload, attach * the payload first before checksum calculation */ if (header_opt == UDP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN) > ETHER_MAX_LEN) payload_len_opt -= (payload_len_opt + ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN) - ETHER_MAX_LEN; /* * go pass pcap generic header, Ethernet header, IP header * and UDP header in new_pkt_data then copy payload_opt * into new_pkt_data and reset pointer to the beginning of * new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN) *new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN) *new_pkt_data--; header->caplen = header->len = ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN; /* update UDP length */ udp_hdr->uh_ulen = htons(header->caplen - (ETHER_HDR_LEN + ip_hlb)); /* update IP total length */ ip_hdr->ip_len = htons(header->caplen - ETHER_HDR_LEN); /* go pass Ethernet header in pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data++; /* * reuse parsing function for IP header to update IP total length in * new_pkt_data and recalculate checksum for IP header if required. */ (void)parse_ip(pkt_data, new_pkt_data, header, ip_hdr, 1); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETHER_HDR_LEN) *pkt_data--; } /* we have no support for checksum calculation for fragmented packet */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* recalculate checksum for UDP header (cover IP pseudo header + UDP header + trailing data) */ if (csum_opt && ip_fo == 0) { /* recalculate checksum if we have enough data */ if (header->caplen >= (ETHER_HDR_LEN + ntohs(ip_hdr->ip_len))) { /* create IP pseudo header */ ipp = (struct ippseudo *)malloc(sizeof(struct ippseudo)); if (ipp == NULL) error("malloc(): cannot allocate memory for ipp"); memcpy(&ipp->ippseudo_src, &ip_hdr->ip_src, sizeof(struct in_addr)); memcpy(&ipp->ippseudo_dst, &ip_hdr->ip_dst, sizeof(struct in_addr)); ipp->ippseudo_pad = 0x00; ipp->ippseudo_p = ip_hdr->ip_p; ipp->ippseudo_len = htons(ntohs(ip_hdr->ip_len) - ip_hlb); udpp_len = sizeof(struct ippseudo) + ntohs(ipp->ippseudo_len); /* udpp_len must be even for correct checksum calculation */ if ((udpp_len % 2) != 0) udpp = (u_char *)malloc(sizeof(u_char) * (udpp_len + 1)); else udpp = (u_char *)malloc(sizeof(u_char) * udpp_len); if (udpp == NULL) error("malloc(): cannot allocate memory for udpp"); if ((udpp_len % 2) != 0) memset(udpp, 0, udpp_len + 1); else memset(udpp, 0, udpp_len); /* copy IP pseudo header from ipp into udpp */ memcpy(udpp, ipp, sizeof(struct ippseudo)); free(ipp); ipp = NULL; /* go pass IP pseudo header in udpp */ i = 0; while (i++ < sizeof(struct ippseudo)) *udpp++; /* clear checksum field */ udp_hdr->uh_sum = 0x0000; /* copy UDP header from udp_hdr into udpp */ memcpy(udpp, udp_hdr, UDP_HDR_LEN); /* reset pointer to the beginning of udpp */ i = 0; while (i++ < sizeof(struct ippseudo)) *udpp--; /* copy trailing data from payload_opt into udpp */ if (layer_opt == 4 && header_opt == UDP && payload_len_opt > 0) { for (i = UDP_HDR_LEN; i < (udpp_len - sizeof(struct ippseudo)); i++) udpp[i + sizeof(struct ippseudo)] = payload_opt[i - UDP_HDR_LEN]; } /* copy trailing data from payload_opt (payload after IP header) into udpp */ else if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { for (i = UDP_HDR_LEN; i < payload_len_opt; i++) udpp[i + sizeof(struct ippseudo)] = payload_opt[i]; } /* copy trailing data from pkt_data into udpp */ else { for (i = UDP_HDR_LEN; i < (udpp_len - sizeof(struct ippseudo)); i++) udpp[i + sizeof(struct ippseudo)] = pkt_data[ETHER_HDR_LEN + ip_hlb + i]; } /* recalculate checksum */ if ((udpp_len % 2) != 0) udp_hdr->uh_sum = cksum(udpp, udpp_len + 1); else udp_hdr->uh_sum = cksum(udpp, udpp_len); udp_hdr->uh_sum = htons(udp_hdr->uh_sum); free(udpp); udpp = NULL; } } free(ip_hdr); ip_hdr = NULL; /* * go pass pcap generic header, Ethernet header and IP header * in new_pkt_data then copy udp_hdr into new_pkt_data and reset * pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data++; memcpy(new_pkt_data, udp_hdr, UDP_HDR_LEN); free(udp_hdr); udp_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETHER_HDR_LEN + ip_hlb) *new_pkt_data--; /* no further editing support after UDP header */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IP header) which covers UDP header + data, * checksum for UDP header corrected above, * while UDP data is written to new_pkt_data in parse_ip() */ else if (layer_opt == 3) return (header->caplen); else return (ETHER_HDR_LEN + ip_hlb + UDP_HDR_LEN); } /* Reference: rfc1071.txt */ u_short cksum(u_char *cp, u_short len) { u_short word_16; /* 16-bit word */ u_int sum = 0; u_short i; /* from 2 adjacent 8-bit words, create a 16-bit word, add all 16-bit words */ for (i = 0; i < len; i = i + 2) { word_16 = ((cp[i] << 8) & 0xff00) + (cp[i + 1] & 0xff); sum += (u_int)word_16; } /* take 16 bits out of the 32-bit sum */ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); /* one's complement the sum */ return ((u_short)~sum); } void info(void) { (void)putchar('\n'); notice("%u packets (%u bytes) written", pkts, bytes); } /* * Reference: tcpdump's util.c * * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * */ void notice(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } } /* * Reference: tcpdump's util.c * * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * */ void error(const char *fmt, ...) { va_list ap; (void)fprintf(stderr, "%s: ", program_name); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } exit(EXIT_FAILURE); } /* * Reference: FreeBSD's /usr/src/lib/libc/net/ether_addr.c * * Copyright (c) 1995 * Bill Paul . All rights reserved. * */ struct ether_addr *ether_aton(const char *a) { int i; static struct ether_addr o; unsigned int o0, o1, o2, o3, o4, o5; i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5); if (i != 6) return (NULL); o.octet[0]=o0; o.octet[1]=o1; o.octet[2]=o2; o.octet[3]=o3; o.octet[4]=o4; o.octet[5]=o5; return ((struct ether_addr *)&o); } /* * Reference: FreeBSD's /usr/src/lib/libc/inet/inet_addr.c * * Copyright (c) 1983, 1990, 1993 * The Regents of the University of California. All rights reserved. * */ int inet_aton(const char *cp, struct in_addr *addr) { u_long val; int base, n; char c; u_int8_t parts[4]; u_int8_t *pp = parts; int digit; c = *cp; for (;;) { /* * Collect number up to ".". * Values are specified as for C: * 0x=hex, 0=octal, isdigit=decimal. */ if (!isdigit((unsigned char)c)) return (0); val = 0; base = 10; digit = 0; if (c == '0') { c = *++cp; if (c == 'x' || c == 'X') base = 16, c = *++cp; else { base = 8; digit = 1; } } for (;;) { if (isascii(c) && isdigit((unsigned char)c)) { if (base == 8 && (c == '8' || c == '9')) return (0); val = (val * base) + (c - '0'); c = *++cp; digit = 1; } else if (base == 16 && isascii(c) && isxdigit((unsigned char)c)) { val = (val << 4) | (c + 10 - (islower((unsigned char)c) ? 'a' : 'A')); c = *++cp; digit = 1; } else break; } if (c == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16 bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 3 || val > 0xffU) return (0); *pp++ = val; c = *++cp; } else break; } /* * Check for trailing characters. */ if (c != '\0' && (!isascii(c) || !isspace((unsigned char)c))) return (0); /* * Did we get a valid digit? */ if (!digit) return (0); /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; switch (n) { case 1: /* a -- 32 bits */ break; case 2: /* a.b -- 8.24 bits */ if (val > 0xffffffU) return (0); val |= parts[0] << 24; break; case 3: /* a.b.c -- 8.8.16 bits */ if (val > 0xffffU) return (0); val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ if (val > 0xffU) return (0); val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } if (addr != NULL) addr->s_addr = htonl(val); return (1); } void usage(void) { (void)fprintf(stderr, "%s version %s\n" "%s\n" "Usage: %s [-I input] [-O output] [-L layer] [-X payload] [-C]\n" " [-M linktype] [-D offset] [-R range] [-S timeframe]\n" " [-T header] [header-specific-options] [-h]\n" "\nOptions:\n" " -I input Input pcap based trace file.\n" " -O output Output trace file.\n" " -L layer Copy up to the specified 'layer' and discard the remaining\n" " data. Value for 'layer' must be either 2, 3 or 4 where\n" " 2 for Ethernet, 3 for ARP or IP, and 4 for ICMP, TCP or UDP.\n" " -X payload Append 'payload' in hex digits to the end of each packet.\n" " Example: -X 0302aad1\n" " -X flag is ignored if -L and -T flag are not specified.\n" " -C Specify this flag to disable checksum correction.\n" " Checksum correction is applicable for non-fragmented IP,\n" " ICMP, TCP, and UDP packets only.\n" " -M linktype Replace the 'linktype' stored in the pcap file header.\n" " Typically, value for 'linktype' is 1 for Ethernet.\n" " Example: -M 12 (for raw IP), -M 51 (for PPPoE)\n" " -D offset Delete the specified byte 'offset' from each packet.\n" " First byte (starting from link layer header) starts from 1.\n" " -L, -X, -C and -T flag are ignored if -D flag is specified.\n" " Example: -D 15-40, -D 10 or -D 18-9999\n" " -R range Save only the specified 'range' of packets.\n" " Example: -R 5-21 or -R 9\n" " -S timeframe Save only the packets within the specified 'timeframe' with\n" " up to one-second resolution using DD/MM/YYYY,HH:MM:SS as the\n" " format for start and end time in 'timeframe'.\n" " Example: -S 22/10/2006,21:47:35-24/10/2006,13:16:05\n" " -S flag is evaluated after -R flag.\n" " -T header Edit only the specified 'header'. Possible keywords for\n" " 'header' are, eth, arp, ip, icmp, tcp, or udp.\n" " -T flag must appear last among the general options.\n" " -h Print version information and usage.\n", program_name, BITTWISTE_VERSION, pcap_lib_version(), program_name); exit(EXIT_SUCCESS); }