Ejemplo n.º 1
0
int registry_uid_assign(struct registry_instance *instance, char* uid) {


	registry_lock();
	
	struct registry_param *p;
	for (p = instance->params; p && strcmp(p->name, "uid"); p = p->next);
	
	if (!p) {
		pomlog(POMLOG_ERR "Could not find the uid parameter");
		registry_unlock();
		return POM_ERR;
	}

	uint32_t old_uid = *PTYPE_UINT32_GETVAL(p->value);

	if (ptype_parse_val(p->value, uid) != POM_OK) {
		pomlog(POMLOG_ERR "Could not parse the new uid");
		registry_unlock();
		return POM_ERR;
	}

	uint32_t new_uid = *PTYPE_UINT32_GETVAL(p->value);

	size_t i;
	for (i = 0; registry_uid_table[i] != old_uid && i < registry_uid_table_size; i++);
	if (i < registry_uid_table_size) {
		registry_uid_table[i] = new_uid;
	}


	registry_unlock();

	return POM_OK;
}
Ejemplo n.º 2
0
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;

}
Ejemplo n.º 3
0
static int input_pcap_interface_open(struct input *i) {

	struct input_pcap_priv *p = i->priv;
	char errbuf[PCAP_ERRBUF_SIZE + 1] = { 0 };

	char *interface = PTYPE_STRING_GETVAL(p->tpriv.iface.p_interface);

	p->p = pcap_create(interface, errbuf);
	if (!p->p) {
		pomlog(POMLOG_ERR "Error opening interface %s : %s", interface, errbuf);
		return POM_ERR;
	}

	char *promisc = PTYPE_BOOL_GETVAL(p->tpriv.iface.p_promisc);
	int err = pcap_set_promisc(p->p, *promisc);
	if (err)
		pomlog(POMLOG_WARN "Error while setting promisc mode : %s", pcap_statustostr(err));

	uint32_t buff_size = *PTYPE_UINT32_GETVAL(p->tpriv.iface.p_buff_size);

	err = pcap_set_buffer_size(p->p, buff_size);
	if (err)
		pomlog(POMLOG_WARN "Error while setting the pcap buffer size : %s", pcap_statustostr(err));

	err = pcap_activate(p->p);

	if (err < 0) {
		pomlog(POMLOG_ERR "Error while activating pcap : %s", pcap_statustostr(err));
		return POM_ERR;
	} else if (err > 0) {
		pomlog(POMLOG_WARN "Warning while activating pcap : %s", pcap_statustostr(err));
	}

	return input_pcap_common_open(i);

}
Ejemplo n.º 4
0
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;

}
Ejemplo n.º 5
0
static int analyzer_tftp_pkt_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) {

	struct analyzer_tftp_priv *priv = obj;

	struct proto_process_stack *s = &stack[stack_index];
	struct proto_process_stack *s_prev = &stack[stack_index - 1];

	uint16_t opcode = *PTYPE_UINT16_GETVAL(s_prev->pkt_info->fields_value[proto_tftp_field_opcode]);

	// Get the session
	struct conntrack_session *session = conntrack_session_get(s_prev->ce);
	if (!session)
		return POM_ERR;

	struct analyzer_tftp_session_priv *spriv = conntrack_session_get_priv(session, obj);

	if (!spriv) {
		// Add session priv if it is not done yet
		spriv = malloc(sizeof(struct analyzer_tftp_session_priv));
		if (!spriv) {
			pom_oom(sizeof(struct analyzer_tftp_session_priv));
			goto err;
		}
		memset(spriv, 0, sizeof(struct analyzer_tftp_session_priv));

		if (conntrack_session_add_priv(session, obj, spriv, analyzer_tftp_session_priv_cleanup) != POM_OK) {
			free(spriv);
			goto err;
		}
	}

	void *pload = s->pload;
	uint32_t plen = s->plen;

	switch (opcode) {
		case tftp_rrq:
		case tftp_wrq: {

			if (plen < 3)
				return POM_OK; // Invalid packet

			// Find the filename
			// The below should always be valid as proto_tftp already checked this
			char *filename = pload; 
			char *mode = memchr(filename, 0, plen - 1) + 1;

			struct analyzer_tftp_file *fq = malloc(sizeof(struct analyzer_tftp_file));
			if (!fq) {
				pom_oom(sizeof(struct analyzer_tftp_file));
				goto err;
			}
			memset(fq, 0, sizeof(struct analyzer_tftp_file));

			// Get the port on which we expect this file
			// No need to check the IP as we got the session biding
			struct proto_process_stack *s_l4 = &stack[stack_index - 2];
			unsigned int i;
			for (i = 0; !fq->port ; i++) {
				struct proto_reg_info *pinfo = proto_get_info(s_l4->proto);
				char *name = pinfo->pkt_fields[i].name;
				if (!name) {
					pomlog(POMLOG_ERR "Source port not found in RRQ/WRQ packets");
					goto err;
				}
				if (!strcmp(name, "sport")) {
					fq->port = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]);
					break;
				}
			}

			fq->evt = event_alloc(priv->evt_file);
			if (!fq->evt) {
				free(fq);
				goto err;
			}
			struct data *evt_data = event_get_data(fq->evt);

			PTYPE_STRING_SETVAL(evt_data[analyzer_tftp_file_filename].value, filename);
			data_set(evt_data[analyzer_tftp_file_filename]);
			PTYPE_STRING_SETVAL(evt_data[analyzer_tftp_file_mode].value, mode);
			data_set(evt_data[analyzer_tftp_file_mode]);
			PTYPE_BOOL_SETVAL(evt_data[analyzer_tftp_file_write].value, opcode == tftp_wrq);
			data_set(evt_data[analyzer_tftp_file_write]);



			fq->next = spriv->files;
			if (fq->next)
				fq->next->prev = fq;
			spriv->files = fq;
			conntrack_session_unlock(session);

			event_process_begin(fq->evt, stack, stack_index, p->ts);

			break;
		}

		case tftp_data: {

			if (plen < sizeof(uint16_t))
				return POM_OK; // Invalid packet

			struct analyzer_tftp_file *f = conntrack_get_priv(s_prev->ce, obj);
			struct data *evt_data = NULL;

			if (!f) {
				// The file is not yet associated to this connection
				// Find it in the queue
				
				struct proto_process_stack *s_l4 = &stack[stack_index - 2];
				unsigned int i;
				uint16_t sport = 0, dport = 0;
				for (i = 0; !sport || !dport ; i++) {
					struct proto_reg_info *pinfo = proto_get_info(s_l4->proto);
					char *name = pinfo->pkt_fields[i].name;
					if (!name) {
						pomlog(POMLOG_ERR "Source port not found in data packets");
						goto err;
					}
					if (!strcmp(name, "sport"))
						sport = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]);

					if (!strcmp(name, "dport"))
						dport = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]);
				}

				// Find the file in the session list
				for (f = spriv->files; ; f = f->next) {
					evt_data = event_get_data(f->evt);
					if (*PTYPE_BOOL_GETVAL(evt_data[analyzer_tftp_file_write].value)) {
						if (f->port == sport)
							break;
					} else {
						if (f->port == dport)
							break;
					}
				}

				if (!f) {
					pomlog(POMLOG_DEBUG "File not found in queued file request.");
					conntrack_session_unlock(session);
					return POM_OK;
				}
				
				// Remove the file from the queue and assign it to the conntrack
				if (f->prev)
					f->prev->next = f->next;
				else
					spriv->files = f->next;
				if (f->next)
					f->next->prev = f->prev;
				
				f->prev = NULL;
				f->next = NULL;

				// Create the payload buffer
				f->pload = pload_alloc(f->evt, PLOAD_FLAG_NEED_MAGIC);
				if (!f->pload)
					goto err;

				conntrack_add_priv(s_prev->ce, obj, f, analyzer_tftp_conntrack_priv_cleanup);
			} else {
				evt_data = event_get_data(f->evt);
			}
			conntrack_session_unlock(session);
		
			if (!f->pload) {
				pomlog(POMLOG_DEBUG "Ignoring extra packet");
				return POM_OK;
			}

			// Discard the block ID
			pload += sizeof(uint16_t);
			plen -= sizeof(uint16_t);

			if (pload_append(f->pload, pload, plen) != POM_OK)
				goto err;

			uint32_t *size = PTYPE_UINT32_GETVAL(evt_data[analyzer_tftp_file_size].value);
			*size += plen;

			if (plen < ANALYZER_TFTP_BLK_SIZE) {
				// Got last packet !
				data_set(evt_data[analyzer_tftp_file_size]);
				
				int res = pload_end(f->pload);
				res += event_process_end(f->evt);
				f->evt = NULL;	
				f->pload = NULL;
				if (res)
					goto err;
			}

			break;
		}

		case tftp_error: {
			conntrack_session_unlock(session);

			struct analyzer_tftp_file *f = conntrack_get_priv(s_prev->ce, obj);
			if (f && f->pload) {
				int res = pload_end(f->pload);
				res += event_process_end(f->evt);
				f->pload = NULL;
				f->evt = NULL;
				if (res)
					goto err;
			}
			break;
		}

		default:
			conntrack_session_unlock(session);
			break;
	}
	
	return POM_OK;

err:
	conntrack_session_unlock(session);
	return POM_ERR;
}
Ejemplo n.º 6
0
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

}
Ejemplo n.º 7
0
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;

}