int encode_attr(const struct l2tp_packet_t *pack, struct l2tp_attr_t *attr,
		const void *val, uint16_t val_len)
{
	uint8_t *u8_ptr = NULL;
	uint8_t md5[MD5_DIGEST_LENGTH];
	MD5_CTX md5_ctx;
	uint16_t pad_len;
	uint16_t attr_type;
	uint16_t blocks_left;
	uint16_t last_block_len;
	int err;

	if (pack->secret == NULL || pack->secret_len == 0) {
		log_error("l2tp: impossible to hide AVP: no secret\n");
		goto err;
	}
	if (pack->last_RV == NULL) {
		log_error("l2tp: impossible to hide AVP: no random vector\n");
		goto err;
	}

	if (u_randbuf(&pad_len, sizeof(pad_len), &err) < 0) {
		if (err)
			log_error("l2tp: impossible to hide AVP:"
				  " reading from urandom failed: %s\n",
				  strerror(err));
		else
			log_error("l2tp: impossible to hide AVP:"
				  " end of file reached while reading"
				  " from urandom\n");
		goto err;
	}
	/* Use at least 16 bytes of padding */
	pad_len = (pad_len & 0x007F) + 16;

	/* Generate Hidden AVP Subformat:
	 *   -original AVP size (2 bytes, network byte order)
	 *   -original AVP value ('val_len' bytes)
	 *   -padding ('pad_len' bytes of random values)
	 */
	attr->length = sizeof(val_len) + val_len + pad_len;
	attr->val.octets = _malloc(attr->length);
	if (attr->val.octets == NULL) {
		log_error("l2tp: impossible to hide AVP:"
			  " memory allocation failed\n");
		goto err;
	}

	*(uint16_t *)attr->val.octets = htons(val_len);
	memcpy(attr->val.octets + sizeof(val_len), val, val_len);

	if (u_randbuf(attr->val.octets + sizeof(val_len) + val_len,
		      pad_len, &err) < 0) {
		if (err)
			log_error("l2tp: impossible to hide AVP:"
				  " reading from urandom failed: %s\n",
				  strerror(err));
		else
			log_error("l2tp: impossible to hide AVP:"
				  " end of file reached while reading"
				  " from urandom\n");
		goto err_free;
	}

	/* Hidden AVP cipher:
	 * ciphered[0] = clear[0] xor MD5(attr_type, secret, RV)
	 * ciphered[1] = clear[1] xor MD5(secret, ciphered[0])
	 * ...
	 * ciphered[n] = clear[n] xor MD5(secret, ciphered[n-1])
	 */
	attr_type = htons(attr->attr->id);
	MD5_Init(&md5_ctx);
	MD5_Update(&md5_ctx, &attr_type, sizeof(attr_type));
	MD5_Update(&md5_ctx, pack->secret, pack->secret_len);
	MD5_Update(&md5_ctx, pack->last_RV->val.octets, pack->last_RV->length);
	MD5_Final(md5, &md5_ctx);

	if (attr->length <= MD5_DIGEST_LENGTH) {
		memxor(attr->val.octets, md5, attr->length);
		return 0;
	}

	memxor(attr->val.octets, md5, MD5_DIGEST_LENGTH);

	blocks_left = attr->length / MD5_DIGEST_LENGTH - 1;
	last_block_len = attr->length % MD5_DIGEST_LENGTH;

	for (u8_ptr = attr->val.octets; blocks_left; --blocks_left) {
		MD5_Init(&md5_ctx);
		MD5_Update(&md5_ctx, pack->secret, pack->secret_len);
		MD5_Update(&md5_ctx, u8_ptr, MD5_DIGEST_LENGTH);
		MD5_Final(md5, &md5_ctx);
		u8_ptr += MD5_DIGEST_LENGTH;
		memxor(u8_ptr, md5, MD5_DIGEST_LENGTH);
	}

	if (last_block_len) {
		MD5_Init(&md5_ctx);
		MD5_Update(&md5_ctx, pack->secret, pack->secret_len);
		MD5_Update(&md5_ctx, u8_ptr, MD5_DIGEST_LENGTH);
		MD5_Final(md5, &md5_ctx);
		memxor(u8_ptr + MD5_DIGEST_LENGTH, md5, last_block_len);
	}

	return 0;

err_free:
	_free(attr->val.octets);
	attr->val.octets = NULL;
err:
	return -1;
}
int encode_attr(const struct l2tp_packet_t *pack, struct l2tp_attr_t *attr,
		const void *val, uint16_t val_len)
{
	uint8_t *u8_ptr = NULL;
	uint8_t md5[MD5_DIGEST_LENGTH];
	MD5_CTX md5_ctx;
	uint16_t pad_len;
	uint16_t attr_type;
	uint16_t blocks_left;
	uint16_t last_block_len;
	int err;

	if (pack->secret == NULL || pack->secret_len == 0) {
		log_error("l2tp: impossible to hide AVP: no secret\n");
		goto err;
	}
	if (pack->last_RV == NULL) {
		log_error("l2tp: impossible to hide AVP: no random vector\n");
		goto err;
	}

	if (u_randbuf(&pad_len, sizeof(pad_len), &err) < 0) {
		if (err)
			log_error("l2tp: impossible to hide AVP:"
				  " reading from urandom failed: %s\n",
				  strerror(err));
		else
			log_error("l2tp: impossible to hide AVP:"
				  " end of file reached while reading"
				  " from urandom\n");
		goto err;
	}
	 
	pad_len = (pad_len & 0x007F) + 16;

	 
	attr->length = sizeof(val_len) + val_len + pad_len;
	attr->val.octets = _malloc(attr->length);
	if (attr->val.octets == NULL) {
		log_error("l2tp: impossible to hide AVP:"
			  " memory allocation failed\n");
		goto err;
	}

	*(uint16_t *)attr->val.octets = htons(val_len);
	memcpy(attr->val.octets + sizeof(val_len), val, val_len);

	if (u_randbuf(attr->val.octets + sizeof(val_len) + val_len,
		      pad_len, &err) < 0) {
		if (err)
			log_error("l2tp: impossible to hide AVP:"
				  " reading from urandom failed: %s\n",
				  strerror(err));
		else
			log_error("l2tp: impossible to hide AVP:"
				  " end of file reached while reading"
				  " from urandom\n");
		goto err_free;
	}

	 
	attr_type = htons(attr->attr->id);
	MD5_Init(&md5_ctx);
	MD5_Update(&md5_ctx, &attr_type, sizeof(attr_type));
	MD5_Update(&md5_ctx, pack->secret, pack->secret_len);
	MD5_Update(&md5_ctx, pack->last_RV->val.octets, pack->last_RV->length);
	MD5_Final(md5, &md5_ctx);

	if (attr->length <= MD5_DIGEST_LENGTH) {
		memxor(attr->val.octets, md5, attr->length);
		return 0;
	}

	memxor(attr->val.octets, md5, MD5_DIGEST_LENGTH);

	blocks_left = attr->length / MD5_DIGEST_LENGTH - 1;
	last_block_len = attr->length % MD5_DIGEST_LENGTH;

	for (u8_ptr = attr->val.octets; blocks_left; --blocks_left) {
		MD5_Init(&md5_ctx);
		MD5_Update(&md5_ctx, pack->secret, pack->secret_len);
		MD5_Update(&md5_ctx, u8_ptr, MD5_DIGEST_LENGTH);
		MD5_Final(md5, &md5_ctx);
		u8_ptr += MD5_DIGEST_LENGTH;
		memxor(u8_ptr, md5, MD5_DIGEST_LENGTH);
	}

	if (last_block_len) {
		MD5_Init(&md5_ctx);
		MD5_Update(&md5_ctx, pack->secret, pack->secret_len);
		MD5_Update(&md5_ctx, u8_ptr, MD5_DIGEST_LENGTH);
		MD5_Final(md5, &md5_ctx);
		memxor(u8_ptr + MD5_DIGEST_LENGTH, md5, last_block_len);
	}

	return 0;

err_free:
	_free(attr->val.octets);
	attr->val.octets = NULL;
err:
	return -1;
}