static int snmp_parse_mangle(unsigned char *msg,
			     u_int16_t len,
			     const struct oct1_map *map,
			     __sum16 *check)
{
	unsigned char *eoc, *end;
	unsigned int cls, con, tag, vers, pdutype;
	struct asn1_ctx ctx;
	struct asn1_octstr comm;
	struct snmp_object **obj;

	if (debug > 1)
		hex_dump(msg, len);

	asn1_open(&ctx, msg, len);

	/*
	 * Start of SNMP message.
	 */
	if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag))
		return 0;
	if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
		return 0;

	/*
	 * Version 1 or 2 handled.
	 */
	if (!asn1_header_decode(&ctx, &end, &cls, &con, &tag))
		return 0;
	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
		return 0;
	if (!asn1_uint_decode (&ctx, end, &vers))
		return 0;
	if (debug > 1)
		printk(KERN_DEBUG "bsalg: snmp version: %u\n", vers + 1);
	if (vers > 1)
		return 1;

	/*
	 * Community.
	 */
	if (!asn1_header_decode (&ctx, &end, &cls, &con, &tag))
		return 0;
	if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OTS)
		return 0;
	if (!asn1_octets_decode(&ctx, end, &comm.data, &comm.len))
		return 0;
	if (debug > 1) {
		unsigned int i;

		printk(KERN_DEBUG "bsalg: community: ");
		for (i = 0; i < comm.len; i++)
			printk("%c", comm.data[i]);
		printk("\n");
	}
	kfree(comm.data);

	/*
	 * PDU type
	 */
	if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &pdutype))
		return 0;
	if (cls != ASN1_CTX || con != ASN1_CON)
		return 0;
	if (debug > 1) {
		static const unsigned char *const pdus[] = {
			[SNMP_PDU_GET] = "get",
			[SNMP_PDU_NEXT] = "get-next",
			[SNMP_PDU_RESPONSE] = "response",
			[SNMP_PDU_SET] = "set",
			[SNMP_PDU_TRAP1] = "trapv1",
			[SNMP_PDU_BULK] = "bulk",
			[SNMP_PDU_INFORM] = "inform",
			[SNMP_PDU_TRAP2] = "trapv2"
		};

		if (pdutype > SNMP_PDU_TRAP2)
			printk(KERN_DEBUG "bsalg: bad pdu type %u\n", pdutype);
		else
			printk(KERN_DEBUG "bsalg: pdu: %s\n", pdus[pdutype]);
	}
	if (pdutype != SNMP_PDU_RESPONSE &&
	    pdutype != SNMP_PDU_TRAP1 && pdutype != SNMP_PDU_TRAP2)
		return 1;

	/*
	 * Request header or v1 trap
	 */
	if (pdutype == SNMP_PDU_TRAP1) {
		struct snmp_v1_trap trap;
		unsigned char ret = snmp_trap_decode(&ctx, &trap, map, check);

		if (ret) {
			kfree(trap.id);
			kfree((unsigned long *)trap.ip_address);
		} else
			return ret;

	} else {
		struct snmp_request req;

		if (!snmp_request_decode(&ctx, &req))
			return 0;

		if (debug > 1)
			printk(KERN_DEBUG "bsalg: request: id=0x%lx error_status=%u "
			"error_index=%u\n", req.id, req.error_status,
			req.error_index);
	}

	/*
	 * Loop through objects, look for IP addresses to mangle.
	 */
	if (!asn1_header_decode(&ctx, &eoc, &cls, &con, &tag))
		return 0;

	if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
		return 0;

	obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC);
	if (obj == NULL) {
		if (net_ratelimit())
			printk(KERN_WARNING "OOM in bsalg(%d)\n", __LINE__);
		return 0;
	}

	while (!asn1_eoc_decode(&ctx, eoc)) {
		unsigned int i;

		if (!snmp_object_decode(&ctx, obj)) {
			if (*obj) {
				kfree((*obj)->id);
				kfree(*obj);
			}
			kfree(obj);
			return 0;
		}

		if (debug > 1) {
			printk(KERN_DEBUG "bsalg: object: ");
			for (i = 0; i < (*obj)->id_len; i++) {
				if (i > 0)
					printk(".");
				printk("%lu", (*obj)->id[i]);
			}
			printk(": type=%u\n", (*obj)->type);

		}

		if ((*obj)->type == SNMP_IPADDR)
			mangle_address(ctx.begin, ctx.pointer - 4 , map, check);

		kfree((*obj)->id);
		kfree(*obj);
	}
	kfree(obj);

	if (!asn1_eoc_decode(&ctx, eoc))
		return 0;

	return 1;
}