void fs_add_ip_fields(fieldset_t *fs, struct iphdr *ip) { fs_add_string(fs, "saddr", make_ip_str(ip->saddr), 1); fs_add_string(fs, "daddr", make_ip_str(ip->daddr), 1); fs_add_uint64(fs, "ipid", ntohl(ip->id)); fs_add_uint64(fs, "ttl", ntohl(ip->ttl)); }
void fs_add_ip_fields(fieldset_t *fs, struct ip *ip) { // WARNING: you must update fs_ip_fields_len as well // as the definitions set (ip_fiels) if you // change the fields added below: fs_add_string(fs, "saddr", make_ip_str(ip->ip_src.s_addr), 1); fs_add_uint64(fs, "saddr_raw", (uint64_t) ip->ip_src.s_addr); fs_add_string(fs, "daddr", make_ip_str(ip->ip_dst.s_addr), 1); fs_add_uint64(fs, "daddr_raw", (uint64_t) ip->ip_dst.s_addr); fs_add_uint64(fs, "ipid", ntohs(ip->ip_id)); fs_add_uint64(fs, "ttl", ip->ip_ttl); }
static bool process_response_question(char **data, uint16_t* data_len, const char* payload, uint16_t payload_len, fieldset_t* list) { // Payload is the start of the DNS packet, including header // data is handle to the start of this RR // data_len is a pointer to the how much total data we have to work with. // This is awful. I'm bad and should feel bad. uint16_t bytes_consumed = 0; char* question_name = get_name(*data, *data_len, payload, payload_len, &bytes_consumed); // Error. if (question_name == NULL) { return 1; } assert(bytes_consumed > 0); if ( (bytes_consumed + sizeof(dns_question_tail)) > *data_len) { free(question_name); return 1; } dns_question_tail* tail = (dns_question_tail*)(*data + bytes_consumed); uint16_t qtype = ntohs(tail->qtype); uint16_t qclass = ntohs(tail->qclass); // Build our new question fieldset fieldset_t *qfs = fs_new_fieldset(); fs_add_unsafe_string(qfs, "name", question_name, 1); fs_add_uint64(qfs, "qtype", qtype); if (qtype > MAX_QTYPE || qtype_qtype_to_strid[qtype] == BAD_QTYPE_VAL) { fs_add_string(qfs, "qtype_str", (char*) BAD_QTYPE_STR, 0); } else { // I've written worse things than this 3rd arg. But I want to be fast. fs_add_string(qfs, "qtype_str", (char*)qtype_strs[qtype_qtype_to_strid[qtype]], 0); } fs_add_uint64(qfs, "qclass", qclass); // Now we're adding the new fs to the list. fs_add_fieldset(list, NULL, qfs); // Now update the pointers. *data = *data + bytes_consumed + sizeof(dns_question_tail); *data_len = *data_len - bytes_consumed - sizeof(dns_question_tail); return 0; }
void fs_add_system_fields(fieldset_t *fs, int is_repeat, int in_cooldown) { fs_add_uint64(fs, "repeat", is_repeat); fs_add_uint64(fs, "cooldown", in_cooldown); char *timestr = xmalloc(TIMESTR_LEN+1); char *timestr_ms = xmalloc(TIMESTR_LEN+1); struct timeval t; gettimeofday(&t, NULL); struct tm *ptm = localtime(&t.tv_sec); strftime(timestr, TIMESTR_LEN, "%Y-%m-%dT%H:%M:%S.%%03d%z", ptm); snprintf(timestr_ms, TIMESTR_LEN, timestr, t.tv_usec/1000); free(timestr); fs_add_string(fs, "timestamp_str", timestr_ms, 1); fs_add_uint64(fs, "timestamp_ts", (uint64_t) t.tv_sec); fs_add_uint64(fs, "timestamp_us", (uint64_t) t.tv_usec); }
void fs_add_system_fields(fieldset_t *fs, int is_repeat, int in_cooldown) { fs_add_uint64(fs, "repeat", is_repeat); fs_add_uint64(fs, "cooldown", in_cooldown); char *timestr = malloc(TIMESTR_LEN+1); if (!timestr) { log_fatal("recv", "unable to allocate memory for " "timestamp string in fieldset."); } struct timeval t; gettimeofday(&t, NULL); struct tm *ptm = localtime(&t.tv_sec); strftime(timestr, TIMESTR_LEN, "%Y-%m-%dT%H:%M:%S%z", ptm); fs_add_string(fs, "timestamp-str", timestr, 1); fs_add_uint64(fs, "timestamp-ts", (uint64_t) t.tv_sec); fs_add_uint64(fs, "timestamp-us", (uint64_t) t.tv_usec); }
void dns_process_packet(const u_char *packet, uint32_t len, fieldset_t *fs, uint32_t *validation) { struct ip *ip_hdr = (struct ip *) &packet[sizeof(struct ether_header)]; if (ip_hdr->ip_p == IPPROTO_UDP) { struct udphdr *udp_hdr = (struct udphdr *) ((char *) ip_hdr + ip_hdr->ip_hl * 4); uint16_t udp_len = ntohs(udp_hdr->uh_ulen); assert(udp_len >= dns_packet_len); char* qname_p = NULL; dns_question_tail* tail_p = NULL; bool is_valid = 0; dns_header* dns_header_p = (dns_header*) &udp_hdr[1]; // verify our dns transaction id if (dns_header_p->id == (validation[2] & 0xFFFF)) { // Verify our question qname_p = (char*) dns_header_p + sizeof(dns_header); tail_p = (dns_question_tail*)(dns_packet + sizeof(dns_header) + qname_len); // Verify our qname if (strcmp(qname, qname_p) == 0) { // Verify the qtype and qclass. if (tail_p->qtype == htons(qtype) && tail_p->qclass \ == htons(0x01)) { is_valid = 1; } } } dns_header* dns_hdr = (dns_header*) &udp_hdr[1]; uint16_t qr = dns_hdr->qr; uint16_t rcode = dns_hdr->rcode; // Success: Has the right validation bits and the right Q // App success: has qr and rcode bits right // Any app level parsing issues: dns_parse_err // High level info fs_add_string(fs, "classification", (char*) "dns", 0); fs_add_uint64(fs, "success", is_valid); fs_add_uint64(fs, "app_success", is_valid && (qr == DNS_QR_ANSWER) && (rcode == DNS_RCODE_NOERR)); // UDP info fs_add_uint64(fs, "sport", ntohs(udp_hdr->uh_sport)); fs_add_uint64(fs, "dport", ntohs(udp_hdr->uh_dport)); fs_add_uint64(fs, "udp_len", udp_len); // ICMP info fs_add_null(fs, "icmp_responder"); fs_add_null(fs, "icmp_type"); fs_add_null(fs, "icmp_code"); fs_add_null(fs, "icmp_unreach_str"); // DNS data if (!is_valid) { // DNS header fs_add_null(fs, "dns_id"); fs_add_null(fs, "dns_rd"); fs_add_null(fs, "dns_tc"); fs_add_null(fs, "dns_aa"); fs_add_null(fs, "dns_opcode"); fs_add_null(fs, "dns_qr"); fs_add_null(fs, "dns_rcode"); fs_add_null(fs, "dns_cd"); fs_add_null(fs, "dns_ad"); fs_add_null(fs, "dns_z"); fs_add_null(fs, "dns_ra"); fs_add_null(fs, "dns_qdcount"); fs_add_null(fs, "dns_ancount"); fs_add_null(fs, "dns_nscount"); fs_add_null(fs, "dns_arcount"); fs_add_repeated(fs, "dns_questions", fs_new_repeated_fieldset()); fs_add_repeated(fs, "dns_answers", fs_new_repeated_fieldset()); fs_add_repeated(fs, "dns_authorities", fs_new_repeated_fieldset()); fs_add_repeated(fs, "dns_additionals", fs_new_repeated_fieldset()); fs_add_uint64(fs, "dns_unconsumed_bytes", 0); fs_add_uint64(fs, "dns_parse_err", 1); } else { // DNS header fs_add_uint64(fs, "dns_id", ntohs(dns_hdr->id)); fs_add_uint64(fs, "dns_rd", dns_hdr->rd); fs_add_uint64(fs, "dns_tc", dns_hdr->tc); fs_add_uint64(fs, "dns_aa", dns_hdr->aa); fs_add_uint64(fs, "dns_opcode", dns_hdr->opcode); fs_add_uint64(fs, "dns_qr", qr); fs_add_uint64(fs, "dns_rcode", rcode); fs_add_uint64(fs, "dns_cd", dns_hdr->cd); fs_add_uint64(fs, "dns_ad", dns_hdr->ad); fs_add_uint64(fs, "dns_z", dns_hdr->z); fs_add_uint64(fs, "dns_ra", dns_hdr->ra); fs_add_uint64(fs, "dns_qdcount", ntohs(dns_hdr->qdcount)); fs_add_uint64(fs, "dns_ancount", ntohs(dns_hdr->ancount)); fs_add_uint64(fs, "dns_nscount", ntohs(dns_hdr->nscount)); fs_add_uint64(fs, "dns_arcount", ntohs(dns_hdr->arcount)); // And now for the complicated part. Hierarchical data. char* data = ((char*)dns_hdr) + sizeof(dns_header); uint16_t data_len = udp_len - sizeof(udp_hdr) - sizeof(dns_header); bool err = 0; // Questions fieldset_t *list = fs_new_repeated_fieldset(); for (int i = 0; i < ntohs(dns_hdr->qdcount) && !err; i++) { err = process_response_question(&data, &data_len, (char*)dns_hdr, udp_len, list); } fs_add_repeated(fs, "dns_questions", list); // Answers list = fs_new_repeated_fieldset(); for (int i = 0; i < ntohs(dns_hdr->ancount) && !err; i++) { err = process_response_answer(&data, &data_len, (char*)dns_hdr, udp_len, list); } fs_add_repeated(fs, "dns_answers", list); // Authorities list = fs_new_repeated_fieldset(); for (int i = 0; i < ntohs(dns_hdr->nscount) && !err; i++) { err = process_response_answer(&data, &data_len, (char*)dns_hdr, udp_len, list); } fs_add_repeated(fs, "dns_authorities", list); // Additionals list = fs_new_repeated_fieldset(); for (int i = 0; i < ntohs(dns_hdr->arcount) && !err; i++) { err = process_response_answer(&data, &data_len, (char*)dns_hdr, udp_len, list); } fs_add_repeated(fs, "dns_additionals", list); // Do we have unconsumed data? fs_add_uint64(fs, "dns_unconsumed_bytes", data_len); if (data_len != 0) { err = 1; } // Did we parse OK? fs_add_uint64(fs, "dns_parse_err", err); } // Now the raw stuff. fs_add_binary(fs, "raw_data", (udp_len - sizeof(struct udphdr)), (void*) &udp_hdr[1], 0); return; } else if (ip_hdr->ip_p == IPPROTO_ICMP) { struct icmp *icmp = (struct icmp*) ((char *) ip_hdr + 4*ip_hdr->ip_hl); struct ip *ip_inner = (struct ip*) ((char *) icmp + ICMP_UNREACH_HEADER_SIZE); // This is the packet we sent struct udphdr *udp_hdr = (struct udphdr *) ((char*) ip_inner + 4*ip_inner->ip_hl); uint16_t udp_len = ntohs(udp_hdr->uh_ulen); // High level info fs_add_string(fs, "classification", (char*) "icmp-unreach", 0); fs_add_uint64(fs, "success", 0); fs_add_uint64(fs, "app_success", 0); // UDP info fs_add_uint64(fs, "sport", ntohs(udp_hdr->uh_sport)); fs_add_uint64(fs, "dport", ntohs(udp_hdr->uh_dport)); fs_add_uint64(fs, "udp_len", udp_len); // ICMP info // XXX This is legacy. not well tested. fs_add_string(fs, "icmp_responder", make_ip_str(ip_hdr->ip_src.s_addr), 1); fs_add_uint64(fs, "icmp_type", icmp->icmp_type); fs_add_uint64(fs, "icmp_code", icmp->icmp_code); if (icmp->icmp_code <= ICMP_UNREACH_PRECEDENCE_CUTOFF) { fs_add_string(fs, "icmp_unreach_str", (char *) udp_unreach_strings[icmp->icmp_code], 0); } else { fs_add_string(fs, "icmp_unreach_str", (char *) "unknown", 0); } // DNS header fs_add_null(fs, "dns_id"); fs_add_null(fs, "dns_rd"); fs_add_null(fs, "dns_tc"); fs_add_null(fs, "dns_aa"); fs_add_null(fs, "dns_opcode"); fs_add_null(fs, "dns_qr"); fs_add_null(fs, "dns_rcode"); fs_add_null(fs, "dns_cd"); fs_add_null(fs, "dns_ad"); fs_add_null(fs, "dns_z"); fs_add_null(fs, "dns_ra"); fs_add_null(fs, "dns_qdcount"); fs_add_null(fs, "dns_ancount"); fs_add_null(fs, "dns_nscount"); fs_add_null(fs, "dns_arcount"); fs_add_repeated(fs, "dns_questions", fs_new_repeated_fieldset()); fs_add_repeated(fs, "dns_answers", fs_new_repeated_fieldset()); fs_add_repeated(fs, "dns_authorities", fs_new_repeated_fieldset()); fs_add_repeated(fs, "dns_additionals", fs_new_repeated_fieldset()); fs_add_uint64(fs, "dns_unconsumed_bytes", 0); fs_add_uint64(fs, "dns_parse_err", 1); fs_add_binary(fs, "raw_data", len, (char*)packet, 0); return; } else { // This should not happen. Both the pcap filter and validate // packet prevent this. log_fatal("dns", "Die. This can only happen if you " "change the pcap filter and don't update the " "process function."); return; } }
static bool process_response_answer(char **data, uint16_t* data_len, const char* payload, uint16_t payload_len, fieldset_t* list) { log_trace("dns", "call to process_response_answer, data_len: %d", *data_len); // Payload is the start of the DNS packet, including header // data is handle to the start of this RR // data_len is a pointer to the how much total data we have to work with. // This is awful. I'm bad and should feel bad. uint16_t bytes_consumed = 0; char* answer_name = get_name(*data, *data_len, payload, payload_len, &bytes_consumed); // Error. if (answer_name == NULL) { return 1; } assert(bytes_consumed > 0); if ( (bytes_consumed + sizeof(dns_answer_tail)) > *data_len) { free(answer_name); return 1; } dns_answer_tail* tail = (dns_answer_tail*)(*data + bytes_consumed); uint16_t type = ntohs(tail->type); uint16_t class = ntohs(tail->class); uint32_t ttl = ntohl(tail->ttl); uint16_t rdlength = ntohs(tail->rdlength); char* rdata = tail->rdata; if ((rdlength + bytes_consumed + sizeof(dns_answer_tail)) > *data_len) { free(answer_name); return 1; } // Build our new question fieldset fieldset_t *afs = fs_new_fieldset(); fs_add_unsafe_string(afs, "name", answer_name, 1); fs_add_uint64(afs, "type", type); if (type > MAX_QTYPE || qtype_qtype_to_strid[type] == BAD_QTYPE_VAL) { fs_add_string(afs, "type_str", (char*) BAD_QTYPE_STR, 0); } else { // I've written worse things than this 3rd arg. But I want to be fast. fs_add_string(afs, "type_str", (char*)qtype_strs[qtype_qtype_to_strid[type]], 0); } fs_add_uint64(afs, "class", class); fs_add_uint64(afs, "ttl", ttl); fs_add_uint64(afs, "rdlength", rdlength); // XXX Fill this out for the other types we care about. if (type == DNS_QTYPE_NS || type == DNS_QTYPE_CNAME) { uint16_t rdata_bytes_consumed = 0; char* rdata_name = get_name(rdata, rdlength, payload, payload_len, &rdata_bytes_consumed); if (rdata_name == NULL) { fs_add_uint64(afs, "rdata_is_parsed", 0); fs_add_binary(afs, "rdata", rdlength, rdata, 0); } else { fs_add_uint64(afs, "rdata_is_parsed", 1); fs_add_unsafe_string(afs, "rdata", rdata_name, 1); } } else if (type == DNS_QTYPE_MX) { uint16_t rdata_bytes_consumed = 0; if (rdlength <= 4) { fs_add_uint64(afs, "rdata_is_parsed", 0); fs_add_binary(afs, "rdata", rdlength, rdata, 0); } else { char* rdata_name = get_name(rdata + 2, rdlength-2, payload, payload_len, &rdata_bytes_consumed); if (rdata_name == NULL) { fs_add_uint64(afs, "rdata_is_parsed", 0); fs_add_binary(afs, "rdata", rdlength, rdata, 0); } else { // (largest value 16bit) + " " + answer + null char* rdata_with_pref = xmalloc(5 + 1 + strlen(rdata_name) + 1); uint8_t num_printed = snprintf(rdata_with_pref, 6, "%hu ", ntohs( *(uint16_t*)rdata)); memcpy(rdata_with_pref + num_printed, rdata_name, strlen(rdata_name)); fs_add_uint64(afs, "rdata_is_parsed", 1); fs_add_unsafe_string(afs, "rdata", rdata_with_pref, 1); } } } else if (type == DNS_QTYPE_TXT) { if (rdlength >= 1 && (rdlength - 1) != *(uint8_t*)rdata ) { log_warn("dns", "TXT record with wrong TXT len. Not processing."); fs_add_uint64(afs, "rdata_is_parsed", 0); fs_add_binary(afs, "rdata", rdlength, rdata, 0); } else { fs_add_uint64(afs, "rdata_is_parsed", 1); char* txt = xmalloc(rdlength); memcpy(txt, rdata + 1, rdlength-1); fs_add_unsafe_string(afs, "rdata", txt, 1); } } else if (type == DNS_QTYPE_A) { if (rdlength != 4) { log_warn("dns", "A record with IP of length %d. Not processing.", rdlength); fs_add_uint64(afs, "rdata_is_parsed", 0); fs_add_binary(afs, "rdata", rdlength, rdata, 0); } else { fs_add_uint64(afs, "rdata_is_parsed", 1); char* addr = strdup(inet_ntoa( *(struct in_addr*)rdata )); fs_add_unsafe_string(afs, "rdata", addr, 1); } } else if (type == DNS_QTYPE_AAAA) { if (rdlength != 16) { log_warn("dns", "AAAA record with IP of length %d. Not processing.", rdlength); fs_add_uint64(afs, "rdata_is_parsed", 0); fs_add_binary(afs, "rdata", rdlength, rdata, 0); } else { fs_add_uint64(afs, "rdata_is_parsed", 1); char* ipv6_str = xmalloc(INET6_ADDRSTRLEN); inet_ntop(AF_INET6, (struct sockaddr_in6*)rdata, ipv6_str,INET6_ADDRSTRLEN); fs_add_unsafe_string(afs, "rdata", ipv6_str, 1); } } else { fs_add_uint64(afs, "rdata_is_parsed", 0); fs_add_binary(afs, "rdata", rdlength, rdata, 0); } // Now we're adding the new fs to the list. fs_add_fieldset(list, NULL, afs); // Now update the pointers. *data = *data + bytes_consumed + sizeof(dns_answer_tail) + rdlength; *data_len = *data_len - bytes_consumed - sizeof(dns_answer_tail) - rdlength; log_trace("dns", "return success from process_response_answer, data_len: %d", *data_len); return 0; }