📄 nf_nat_snmp_basic.c
字号:
unsigned int cls, con, tag, len, idlen; unsigned short type; unsigned char *eoc, *end, *p; unsigned long *lp, *id; unsigned long ul; long l; *obj = NULL; id = NULL; if (!asn1_header_decode(ctx, &eoc, &cls, &con, &tag)) return 0; if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) return 0; if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) return 0; if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI) return 0; if (!asn1_oid_decode(ctx, end, &id, &idlen)) return 0; if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) { kfree(id); return 0; } if (con != ASN1_PRI) { kfree(id); return 0; } type = 0; if (!snmp_tag_cls2syntax(tag, cls, &type)) { kfree(id); return 0; } l = 0; switch (type) { case SNMP_INTEGER: len = sizeof(long); if (!asn1_long_decode(ctx, end, &l)) { kfree(id); return 0; } *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); if (*obj == NULL) { kfree(id); if (net_ratelimit()) printk("OOM in bsalg (%d)\n", __LINE__); return 0; } (*obj)->syntax.l[0] = l; break; case SNMP_OCTETSTR: case SNMP_OPAQUE: if (!asn1_octets_decode(ctx, end, &p, &len)) { kfree(id); return 0; } *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); if (*obj == NULL) { kfree(id); if (net_ratelimit()) printk("OOM in bsalg (%d)\n", __LINE__); return 0; } memcpy((*obj)->syntax.c, p, len); kfree(p); break; case SNMP_NULL: case SNMP_NOSUCHOBJECT: case SNMP_NOSUCHINSTANCE: case SNMP_ENDOFMIBVIEW: len = 0; *obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC); if (*obj == NULL) { kfree(id); if (net_ratelimit()) printk("OOM in bsalg (%d)\n", __LINE__); return 0; } if (!asn1_null_decode(ctx, end)) { kfree(id); kfree(*obj); *obj = NULL; return 0; } break; case SNMP_OBJECTID: if (!asn1_oid_decode(ctx, end, (unsigned long **)&lp, &len)) { kfree(id); return 0; } len *= sizeof(unsigned long); *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); if (*obj == NULL) { kfree(lp); kfree(id); if (net_ratelimit()) printk("OOM in bsalg (%d)\n", __LINE__); return 0; } memcpy((*obj)->syntax.ul, lp, len); kfree(lp); break; case SNMP_IPADDR: if (!asn1_octets_decode(ctx, end, &p, &len)) { kfree(id); return 0; } if (len != 4) { kfree(p); kfree(id); return 0; } *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); if (*obj == NULL) { kfree(p); kfree(id); if (net_ratelimit()) printk("OOM in bsalg (%d)\n", __LINE__); return 0; } memcpy((*obj)->syntax.uc, p, len); kfree(p); break; case SNMP_COUNTER: case SNMP_GAUGE: case SNMP_TIMETICKS: len = sizeof(unsigned long); if (!asn1_ulong_decode(ctx, end, &ul)) { kfree(id); return 0; } *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); if (*obj == NULL) { kfree(id); if (net_ratelimit()) printk("OOM in bsalg (%d)\n", __LINE__); return 0; } (*obj)->syntax.ul[0] = ul; break; default: kfree(id); return 0; } (*obj)->syntax_len = len; (*obj)->type = type; (*obj)->id = id; (*obj)->id_len = idlen; if (!asn1_eoc_decode(ctx, eoc)) { kfree(id); kfree(*obj); *obj = NULL; return 0; } return 1;}static unsigned char snmp_request_decode(struct asn1_ctx *ctx, struct snmp_request *request){ unsigned int cls, con, tag; unsigned char *end; 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_ulong_decode(ctx, end, &request->id)) return 0; 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, &request->error_status)) return 0; 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, &request->error_index)) return 0; return 1;}/* * Fast checksum update for possibly oddly-aligned UDP byte, from the * code example in the draft. */static void fast_csum(__sum16 *csum, const unsigned char *optr, const unsigned char *nptr, int offset){ unsigned char s[4]; if (offset & 1) { s[0] = s[2] = 0; s[1] = ~*optr; s[3] = *nptr; } else { s[1] = s[3] = 0; s[0] = ~*optr; s[2] = *nptr; } *csum = csum_fold(csum_partial(s, 4, ~csum_unfold(*csum)));}/* * Mangle IP address. * - begin points to the start of the snmp messgae * - addr points to the start of the address */static inline void mangle_address(unsigned char *begin, unsigned char *addr, const struct oct1_map *map, __sum16 *check){ if (map->from == NOCT1(addr)) { u_int32_t old; if (debug) memcpy(&old, (unsigned char *)addr, sizeof(old)); *addr = map->to; /* Update UDP checksum if being used */ if (*check) { fast_csum(check, &map->from, &map->to, addr - begin); } if (debug) printk(KERN_DEBUG "bsalg: mapped %u.%u.%u.%u to " "%u.%u.%u.%u\n", NIPQUAD(old), NIPQUAD(*addr)); }}static unsigned char snmp_trap_decode(struct asn1_ctx *ctx, struct snmp_v1_trap *trap, const struct oct1_map *map, __sum16 *check){ unsigned int cls, con, tag, len; unsigned char *end; if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) return 0; if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI) return 0; if (!asn1_oid_decode(ctx, end, &trap->id, &trap->id_len)) return 0; if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) goto err_id_free; if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) || (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS))) goto err_id_free; if (!asn1_octets_decode(ctx, end, (unsigned char **)&trap->ip_address, &len)) goto err_id_free; /* IPv4 only */ if (len != 4) goto err_addr_free; mangle_address(ctx->begin, ctx->pointer - 4, map, check); if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) goto err_addr_free; if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) goto err_addr_free; if (!asn1_uint_decode(ctx, end, &trap->general)) goto err_addr_free; if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) goto err_addr_free; if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) goto err_addr_free; if (!asn1_uint_decode(ctx, end, &trap->specific)) goto err_addr_free; if (!asn1_header_decode(ctx, &end, &cls, &con, &tag)) goto err_addr_free; if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) || (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT))) goto err_addr_free; if (!asn1_ulong_decode(ctx, end, &trap->time)) goto err_addr_free; return 1;err_addr_free: kfree((unsigned long *)trap->ip_address);err_id_free: kfree(trap->id); return 0;}/***************************************************************************** * * Misc. routines * *****************************************************************************/static void hex_dump(unsigned char *buf, size_t len){ size_t i; for (i = 0; i < len; i++) { if (i && !(i % 16)) printk("\n"); printk("%02x ", *(buf + i)); } printk("\n");}/* * Parse and mangle SNMP message according to mapping. * (And this is the fucking 'basic' method). */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) { unsigned char *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;}/***************************************************************************** * * NAT routines. * *****************************************************************************//* * SNMP translation routine. */static int snmp_translate(struct nf_conn *ct, enum ip_conntrack_info ctinfo, struct sk_buff *skb){ struct iphdr *iph = ip_hdr(skb); struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl); u_int16_t udplen = ntohs(udph->len); u_int16_t paylen = udplen - sizeof(struct udphdr); int dir = CTINFO2DIR(ctinfo); struct oct1_map map; /* * Determine mappping for application layer addresses based * on NAT manipulations for the packet. */ if (dir == IP_CT_DIR_ORIGINAL) { /* SNAT traps */ map.from = NOCT1(&ct->tuplehash[dir].tuple.src.u3.ip); map.to = NOCT1(&ct->tuplehash[!dir].tuple.dst.u3.ip); } else { /* DNAT replies */ map.from = NOCT1(&ct->tuplehash[dir].tuple.src.u3.ip); map.to = NOCT1(&ct->tuplehash[!dir].tuple.dst.u3.ip); } if (map.from == map.to) return NF_ACCEPT; if (!snmp_parse_mangle((unsigned char *)udph + sizeof(struct udphdr), paylen, &map, &udph->check)) { if (net_ratelimit()) printk(KERN_WARNING "bsalg: parser failed\n"); return NF_DROP; } return NF_ACCEPT;}/* We don't actually set up expectations, just adjust internal IP * addresses if this is being NATted */static int help(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo){ int dir = CTINFO2DIR(ctinfo); unsigned int ret; struct iphdr *iph = ip_hdr(skb); struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); /* SNMP replies and originating SNMP traps get mangled */ if (udph->source == htons(SNMP_PORT) && dir != IP_CT_DIR_REPLY) return NF_ACCEPT; if (udph->dest == htons(SNMP_TRAP_PORT) && dir != IP_CT_DIR_ORIGINAL) return NF_ACCEPT; /* No NAT? */ if (!(ct->status & IPS_NAT_MASK)) return NF_ACCEPT; /* * Make sure the packet length is ok. So far, we were only guaranteed * to have a valid length IP header plus 8 bytes, which means we have * enough room for a UDP header. Just verify the UDP length field so we * can mess around with the payload. */ if (ntohs(udph->len) != skb->len - (iph->ihl << 2)) { if (net_ratelimit()) printk(KERN_WARNING "SNMP: dropping malformed packet " "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n", NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); return NF_DROP; } if (!skb_make_writable(skb, skb->len)) return NF_DROP; spin_lock_bh(&snmp_lock); ret = snmp_translate(ct, ctinfo, skb); spin_unlock_bh(&snmp_lock); return ret;}static struct nf_conntrack_helper snmp_helper __read_mostly = { .max_expected = 0, .timeout = 180, .me = THIS_MODULE, .help = help, .name = "snmp", .tuple.src.l3num = AF_INET, .tuple.src.u.udp.port = __constant_htons(SNMP_PORT), .tuple.dst.protonum = IPPROTO_UDP,};static struct nf_conntrack_helper snmp_trap_helper __read_mostly = { .max_expected = 0, .timeout = 180, .me = THIS_MODULE, .help = help, .name = "snmp_trap", .tuple.src.l3num = AF_INET, .tuple.src.u.udp.port = __constant_htons(SNMP_TRAP_PORT), .tuple.dst.protonum = IPPROTO_UDP,};/***************************************************************************** * * Module stuff. * *****************************************************************************/static int __init nf_nat_snmp_basic_init(void){ int ret = 0; ret = nf_conntrack_helper_register(&snmp_helper); if (ret < 0) return ret; ret = nf_conntrack_helper_register(&snmp_trap_helper); if (ret < 0) { nf_conntrack_helper_unregister(&snmp_helper); return ret; } return ret;}static void __exit nf_nat_snmp_basic_fini(void){ nf_conntrack_helper_unregister(&snmp_helper); nf_conntrack_helper_unregister(&snmp_trap_helper);}module_init(nf_nat_snmp_basic_init);module_exit(nf_nat_snmp_basic_fini);module_param(debug, int, 0600);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -