static int proto_udp_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; if (sizeof(struct udphdr) > s->plen) return PROTO_INVALID; struct udphdr *hdr = s->pload; uint16_t ulen = ntohs(hdr->uh_ulen); uint16_t sport = ntohs(hdr->uh_sport); uint16_t dport = ntohs(hdr->uh_dport); if (ulen > s->plen) return PROTO_INVALID; PTYPE_UINT16_SETVAL(s->pkt_info->fields_value[proto_udp_field_sport], sport); PTYPE_UINT16_SETVAL(s->pkt_info->fields_value[proto_udp_field_dport], dport); if (conntrack_get(stack, stack_index) != POM_OK) return POM_ERR; int res = POM_ERR; struct proto_process_stack *s_next = &stack[stack_index + 1]; if (s->ce->children) { res = conntrack_delayed_cleanup(s->ce, 0, p->ts); s_next->proto = s->ce->children->ce->proto; } else { uint32_t *conntrack_timeout = PTYPE_UINT32_GETVAL(param_conntrack_timeout); res = conntrack_delayed_cleanup(s->ce, *conntrack_timeout, p->ts); } conntrack_unlock(s->ce); s_next->pload = s->pload + sizeof(struct udphdr); s_next->plen = ulen - sizeof(struct udphdr); if (!s_next->proto) { if (dport == 53 || sport == 53) s_next->proto = proto_dns; if (dport == 69 || sport == 69) s_next->proto = proto_tftp; } return res; }
static int proto_ipv4_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_next = &stack[stack_index + 1]; struct ip* hdr = s->pload; unsigned int hdr_len = hdr->ip_hl * 4; if (s->plen < sizeof(struct ip) || // length smaller than header hdr->ip_hl < 5 || // ip header < 5 bytes ntohs(hdr->ip_len) < hdr_len || // datagram size < ip header length ntohs(hdr->ip_len) > s->plen) { // datagram size > given size return PROTO_INVALID; } PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_src], hdr->ip_src); PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_dst], hdr->ip_dst); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_tos], hdr->ip_tos); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_ttl], hdr->ip_ttl); // Handle conntrack stuff if (conntrack_get(stack, stack_index) != POM_OK) return PROTO_ERR; s_next->pload = s->pload + hdr_len; s_next->plen = ntohs(hdr->ip_len) - hdr_len; s_next->proto = proto_get_by_number(s->proto, hdr->ip_p); int res = POM_ERR; if (s->ce->children) { res = conntrack_delayed_cleanup(s->ce, 0, p->ts); } else { uint32_t *conntrack_timeout = PTYPE_UINT32_GETVAL(param_conntrack_timeout); res = conntrack_delayed_cleanup(s->ce, *conntrack_timeout, p->ts); } if (res == POM_ERR) { conntrack_unlock(s->ce); return PROTO_ERR; } uint16_t frag_off = ntohs(hdr->ip_off); // Check if packet is fragmented and need more handling if (frag_off & IP_DONT_FRAG) { conntrack_unlock(s->ce); return PROTO_OK; // Nothing to do } if (!(frag_off & IP_MORE_FRAG) && !(frag_off & IP_OFFSET_MASK)) { conntrack_unlock(s->ce); return PROTO_OK; // Nothing to do, full packet } uint16_t offset = (frag_off & IP_OFFSET_MASK) << 3; size_t frag_size = ntohs(hdr->ip_len) - (hdr->ip_hl * 4); // Ignore invalid fragments if (frag_size > 0xFFFF) { conntrack_unlock(s->ce); return PROTO_INVALID; } if (frag_size > s->plen + hdr_len) { conntrack_unlock(s->ce); return PROTO_INVALID; } // Account for one more fragment registry_perf_inc(perf_frags, 1); struct proto_ipv4_fragment *tmp = s->ce->priv; // Let's find the right buffer for (; tmp && tmp->id != hdr->ip_id; tmp = tmp->next); if (!tmp) { // Buffer not found, create it tmp = malloc(sizeof(struct proto_ipv4_fragment)); if (!tmp) { pom_oom(sizeof(struct proto_ipv4_fragment)); conntrack_unlock(s->ce); return PROTO_ERR; } memset(tmp, 0, sizeof(struct proto_ipv4_fragment)); tmp->t = conntrack_timer_alloc(s->ce, proto_ipv4_fragment_cleanup, tmp); if (!tmp->t) { conntrack_unlock(s->ce); free(tmp); return PROTO_ERR; } tmp->id = hdr->ip_id; if (!s_next->proto) { // Set processed flag so no attempt to process this will be done tmp->flags |= PROTO_IPV4_FLAG_PROCESSED; conntrack_unlock(s->ce); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_STOP; } tmp->multipart = packet_multipart_alloc(s_next->proto, 0); if (!tmp->multipart) { conntrack_unlock(s->ce); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_ERR; } tmp->next = s->ce->priv; if (tmp->next) tmp->next->prev = tmp; s->ce->priv = tmp; } // Fragment was already handled if (tmp->flags & PROTO_IPV4_FLAG_PROCESSED) { conntrack_unlock(s->ce); registry_perf_inc(perf_frags_dropped, 1); return PROTO_STOP; } // Add the fragment if (packet_multipart_add_packet(tmp->multipart, p, offset, frag_size, (s->pload - (void*)p->buff) + (hdr->ip_hl * 4)) != POM_OK) { conntrack_unlock(s->ce); packet_multipart_cleanup(tmp->multipart); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_ERR; } tmp->count++; // Schedule the timeout for the fragment uint32_t *frag_timeout = PTYPE_UINT32_GETVAL(param_frag_timeout); conntrack_timer_queue(tmp->t, *frag_timeout, p->ts); if (!(frag_off & IP_MORE_FRAG)) tmp->flags |= PROTO_IPV4_FLAG_GOT_LAST; if ((tmp->flags & PROTO_IPV4_FLAG_GOT_LAST) && !tmp->multipart->gaps) tmp->flags |= PROTO_IPV4_FLAG_PROCESSED; conntrack_unlock(s->ce); if ((tmp->flags & PROTO_IPV4_FLAG_PROCESSED)) { int res = packet_multipart_process(tmp->multipart, stack, stack_index + 1); tmp->multipart = NULL; // Multipart will be cleared automatically if (res == PROTO_ERR) { return PROTO_ERR; } else if (res == PROTO_INVALID) { registry_perf_inc(perf_frags_dropped, tmp->count); } else { registry_perf_inc(perf_reassembled_pkts, 1); } } return PROTO_STOP; // Stop processing the packet }
static int proto_eap_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; if (sizeof(struct eap_header) > s->plen) return PROTO_INVALID; struct proto_eap_priv *priv = proto_priv; struct eap_header *hdr = s->pload; PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_eap_field_code], hdr->code); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_eap_field_identifier], hdr->identifier); if (hdr->code < 1 || hdr->code > 4) return PROTO_INVALID; uint16_t len = ntohs(hdr->length); if (len > s->plen) return PROTO_INVALID; // Keep only the payload lenght len -= sizeof(struct eap_header); if (conntrack_get(stack, stack_index) != POM_OK) return PROTO_ERR; if (conntrack_delayed_cleanup(s->ce, *PTYPE_UINT32_GETVAL(priv->p_timeout), p->ts) != POM_OK) { conntrack_unlock(s->ce); return PROTO_ERR; } conntrack_unlock(s->ce); if (hdr->code == 3 || hdr->code == 4) { // Content length is 0 for success and failure if (len != 4) return PROTO_INVALID; len = 0; if (!event_has_listener(priv->evt_success_failure)) return PROTO_OK; struct event *evt = event_alloc(priv->evt_success_failure); if (!evt) return PROTO_ERR; struct data *evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_identifier].value, hdr->identifier); data_set(evt_data[evt_eap_common_identifier]); PTYPE_BOOL_SETVAL(evt_data[evt_eap_success_failure_success].value, (hdr->code == 3 ? 1 : 0)); data_set(evt_data[evt_eap_success_failure_success]); return event_process(evt, stack, stack_index, p->ts); } // At this point, code is either 1 or 2 (request/response) void *pload = s->pload + sizeof(struct eap_header); uint8_t type = 0; // There is at least 1 byte of data for request/response if (len < 1) return PROTO_INVALID; len--; type = *(uint8_t*)pload; pload++; struct event *evt = NULL; struct data *evt_data = NULL; switch (type) { case 1: // Identity if (!event_has_listener(priv->evt_identity)) break; evt = event_alloc(priv->evt_identity); if (!evt) return PROTO_ERR; if (len) { evt_data = event_get_data(evt); PTYPE_STRING_SETVAL_N(evt_data[evt_eap_identity_identity].value, pload, len); data_set(evt_data[evt_eap_identity_identity]); } break; case 4: // MD5-Challenge if (!event_has_listener(priv->evt_md5_challenge)) break; if (len < 17) return PROTO_INVALID; uint8_t value_size = *(uint8_t*)pload; if (value_size != 16) return PROTO_INVALID; pload++; len--; evt = event_alloc(priv->evt_md5_challenge); if (!evt) return PROTO_ERR; evt_data = event_get_data(evt); PTYPE_BYTES_SETLEN(evt_data[evt_eap_md5_challenge_value].value, 16); PTYPE_BYTES_SETVAL(evt_data[evt_eap_md5_challenge_value].value, pload); data_set(evt_data[evt_eap_md5_challenge_value]); if (len > 16) { PTYPE_STRING_SETVAL_N(evt_data[evt_eap_md5_challenge_name].value, pload + 16, len - 16); data_set(evt_data[evt_eap_md5_challenge_name]); } break; } if (evt) { if (!evt_data) evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_identifier].value, hdr->identifier); data_set(evt_data[evt_eap_common_identifier]); PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_code].value, hdr->code); data_set(evt_data[evt_eap_common_code]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) return PROTO_ERR; } return PROTO_OK; }
static int proto_ppp_pap_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; if (sizeof(struct ppp_pap_header) > s->plen) return PROTO_INVALID; struct ppp_pap_header *pchdr = s->pload; size_t len = ntohs(pchdr->length); if (len > s->plen) return PROTO_INVALID; // Keep only the payload len len -= sizeof(struct ppp_pap_header); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ppp_pap_field_code], pchdr->code); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ppp_pap_field_identifier], pchdr->identifier); struct proto_ppp_pap_priv *priv = proto_priv; if (conntrack_get(stack, stack_index) != POM_OK) return PROTO_ERR; if (conntrack_delayed_cleanup(s->ce, *PTYPE_UINT32_GETVAL(priv->p_auth_timeout), p->ts) != POM_OK) { conntrack_unlock(s->ce); return PROTO_ERR; } conntrack_unlock(s->ce); if (pchdr->code == 1 && event_has_listener(priv->evt_request)) { if (len < 4) return PROTO_INVALID; uint8_t *peer_id_len = s->pload + sizeof(struct ppp_pap_header); if (*peer_id_len > len - 2) return PROTO_INVALID; len -= (*peer_id_len + 1); uint8_t *pwd_len = peer_id_len + *peer_id_len + 1; if (*pwd_len > len - 1) return PROTO_INVALID; // Process the challenge/response event struct event *evt = event_alloc(priv->evt_request); if (!evt) return PROTO_ERR; struct data *evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_request_code].value, pchdr->code); data_set(evt_data[evt_ppp_pap_request_code]); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_request_identifier].value, pchdr->identifier); data_set(evt_data[evt_ppp_pap_request_identifier]); PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_request_peer_id].value, (char *)peer_id_len + 1, *peer_id_len); data_set(evt_data[evt_ppp_pap_request_peer_id]); PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_request_password].value, (char *)pwd_len + 1, *pwd_len); data_set(evt_data[evt_ppp_pap_request_password]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) return PROTO_ERR; } if ((pchdr->code == 2 || pchdr->code == 3) && event_has_listener(priv->evt_ack_nack)) { struct event *evt = event_alloc(priv->evt_ack_nack); if (!evt) return PROTO_ERR; struct data *evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_ack_nack_code].value, pchdr->code); data_set(evt_data[evt_ppp_pap_ack_nack_code]); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_ack_nack_identifier].value, pchdr->identifier); data_set(evt_data[evt_ppp_pap_ack_nack_identifier]); uint8_t *msg_len = s->pload + sizeof(struct ppp_pap_header); if (*msg_len > len - 1) return PROTO_INVALID; PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_ack_nack_message].value, (char *)msg_len + 1, *msg_len); data_set(evt_data[evt_ppp_pap_ack_nack_message]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) return PROTO_ERR; } return PROTO_OK; }