+static int eap_tnc_process_cont(struct eap_tnc_data *data,
+ const u8 *buf, size_t len)
+{
+ /* Process continuation of a pending message */
+ if (len > wpabuf_tailroom(data->in_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
+ data->state = FAIL;
+ return -1;
+ }
+
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
+ "bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+
+ return 0;
+}
+
+
+static int eap_tnc_process_fragment(struct eap_tnc_data *data,
+ u8 flags, u32 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
+ "fragmented packet");
+ return -1;
+ }
+
+ if (data->in_buf == NULL) {
+ /* First fragment of the message */
+ data->in_buf = wpabuf_alloc(message_length);
+ if (data->in_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
+ "message");
+ return -1;
+ }
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
+ "fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+ }
+
+ return 0;
+}
+
+
+static void eap_tnc_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_tnc_data *data = priv;
+ const u8 *pos, *end;
+ size_t len;
+ u8 flags;
+ u32 message_length = 0;
+ struct wpabuf tmpbuf;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
+ if (pos == NULL)
+ return; /* Should not happen; message already verified */
+
+ end = pos + len;
+
+ if (len == 1 && (data->state == DONE || data->state == FAIL)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
+ "message");
+ return;
+ }
+
+ if (len == 0) {
+ /* fragment ack */
+ flags = 0;
+ } else
+ flags = *pos++;
+
+ if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
+ data->state = FAIL;
+ return;
+ }
+ message_length = WPA_GET_BE32(pos);
+ pos += 4;
+
+ if (message_length < (u32) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
+ "Length (%d; %ld remaining in this msg)",
+ message_length, (long) (end - pos));
+ data->state = FAIL;
+ return;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
+ "Message Length %u", flags, message_length);
+
+ if (data->state == WAIT_FRAG_ACK) {
+ if (len != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
+ "in WAIT_FRAG_ACK state");
+ data->state = FAIL;
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
+ data->state = CONTINUE;
+ return;
+ }
+
+ if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
+ data->state = FAIL;
+ return;
+ }
+
+ if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
+ if (eap_tnc_process_fragment(data, flags, message_length,
+ pos, end - pos) < 0)
+ data->state = FAIL;
+ else
+ data->state = FRAG_ACK;
+ return;
+ } else if (data->state == FRAG_ACK) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
+ data->state = CONTINUE;
+ }
+
+ if (data->in_buf == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&tmpbuf, pos, end - pos);
+ data->in_buf = &tmpbuf;
+ }
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
+ wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
+ tncs_process(data, data->in_buf);
+
+ if (data->in_buf != &tmpbuf)
+ wpabuf_free(data->in_buf);
+ data->in_buf = NULL;
+}
+
+