void print_snatlog(struct nf_conntrack *ct, time_t *timestamp, char *proto_str) { int ret = 0, size = 0, offset = 0, len = BUF_LEN; char buf[BUF_LEN]; ret = __snprintf_start_log(buf, len, "SNAT_LOG"); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " proto=%s", proto_str); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " o-src=%s", net2addr(nfct_get_attr_u32(ct,ATTR_ORIG_IPV4_SRC))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " o-spt=%d", ntohs(nfct_get_attr_u16(ct,ATTR_ORIG_PORT_SRC))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " t-src=%s", net2addr(nfct_get_attr_u32(ct,ATTR_REPL_IPV4_DST))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " t-spt=%d", ntohs(nfct_get_attr_u16(ct,ATTR_REPL_PORT_DST))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " duration=%.0lfs", difftime(time(NULL),*timestamp)); BUFFER_SIZE(ret, size, len, offset); buf[size+1 > len ? len-1 : size] = '\0'; write_msg(LOG_INFO, buf); }
static inline void ct_build_u32(const struct nf_conntrack *ct, int a, struct nethdr *n, int b) { uint32_t data = nfct_get_attr_u32(ct, a); data = htonl(data); addattr(n, b, &data, sizeof(uint32_t)); }
static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) { unsigned int *ret = (unsigned int *)data; *ret = nfct_get_attr_u32(ct, ATTR_MARK); (void)type; /* eliminate warning */ gotit = 1; return NFCT_CB_CONTINUE; }
static PyObject* get_attr32(struct nf_conntrack *ct, int attrid, int ntoh) { uint32_t value; value = nfct_get_attr_u32(ct, attrid); if (ntoh) value = ntohl(value); return PyLong_FromUnsignedLong(value); }
static int event_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) { struct conntrack_list *no; u_int8_t l4proto; // we are interested only in SNAT connections if (!nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT)) return NFCT_CB_CONTINUE; // We are interested only in TCP/UDP L4 protocols... l4proto = nfct_get_attr_u8(ct,ATTR_ORIG_L4PROTO); if (l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP) return NFCT_CB_CONTINUE; if (verbose_flag) { print_verbose(ct, type, proto_str(l4proto)); } switch(type) { case NFCT_T_NEW: no = (struct conntrack_list *)malloc(sizeof(struct conntrack_list)); no->id = nfct_get_attr_u32(ct,ATTR_ID); no->orig_ipv4_src = nfct_get_attr_u32(ct,ATTR_ORIG_IPV4_SRC); no->orig_port_src = nfct_get_attr_u16(ct,ATTR_ORIG_PORT_SRC); time(&no->timestamp); list_add(&ct_list, no); break; case NFCT_T_DESTROY: no = list_find(ct_list, nfct_get_attr_u32(ct,ATTR_ID), nfct_get_attr_u32(ct,ATTR_ORIG_IPV4_SRC), nfct_get_attr_u16(ct,ATTR_ORIG_PORT_SRC)); if (no) { print_snatlog(ct, &no->timestamp, proto_str(l4proto)); list_del(&ct_list,no); } break; default: break; } return NFCT_CB_CONTINUE; }
int nl_create_conntrack(struct nfct_handle *h, const struct nf_conntrack *orig, int timeout) { int ret; struct nf_conntrack *ct; ct = nfct_clone(orig); if (ct == NULL) return -1; if (timeout > 0) nfct_set_attr_u32(ct, ATTR_TIMEOUT, timeout); /* we hit error if we try to change the expected bit */ if (nfct_attr_is_set(ct, ATTR_STATUS)) { uint32_t status = nfct_get_attr_u32(ct, ATTR_STATUS); status &= ~IPS_EXPECTED; nfct_set_attr_u32(ct, ATTR_STATUS, status); } nfct_setobjopt(ct, NFCT_SOPT_SETUP_REPLY); /* disable TCP window tracking for recovered connections if required */ if (nfct_attr_is_set(ct, ATTR_TCP_STATE)) { uint8_t flags = IP_CT_TCP_FLAG_SACK_PERM; if (!CONFIG(sync).tcp_window_tracking) flags |= IP_CT_TCP_FLAG_BE_LIBERAL; else flags |= IP_CT_TCP_FLAG_WINDOW_SCALE; /* FIXME: workaround, we should send TCP flags in updates */ if (nfct_get_attr_u8(ct, ATTR_TCP_STATE) >= TCP_CONNTRACK_TIME_WAIT) { flags |= IP_CT_TCP_FLAG_CLOSE_INIT; } nfct_set_attr_u8(ct, ATTR_TCP_FLAGS_ORIG, flags); nfct_set_attr_u8(ct, ATTR_TCP_MASK_ORIG, flags); nfct_set_attr_u8(ct, ATTR_TCP_FLAGS_REPL, flags); nfct_set_attr_u8(ct, ATTR_TCP_MASK_REPL, flags); } ret = nfct_query(h, NFCT_Q_CREATE, ct); nfct_destroy(ct); return ret; }
static void __do_commit_step(struct __commit_container *tmp, struct cache_object *obj) { int ret, retry = 1, timeout; struct nf_conntrack *ct = obj->ct; if (CONFIG(commit_timeout)) { timeout = CONFIG(commit_timeout); } else { timeout = time(NULL) - obj->lastupdate; if (timeout < 0) { /* XXX: Arbitrarily set the timer to one minute, how * can this happen? For example, an adjustment due to * daylight-saving. Probably other situations can * trigger this. */ timeout = 60; } /* calculate an estimation of the current timeout */ timeout = nfct_get_attr_u32(ct, ATTR_TIMEOUT) - timeout; if (timeout < 0) { timeout = 60; } } retry: if (nl_create_conntrack(tmp->h, ct, timeout) == -1) { if (errno == EEXIST && retry == 1) { ret = nl_destroy_conntrack(tmp->h, ct); if (ret == 0 || (ret == -1 && errno == ENOENT)) { if (retry) { retry = 0; goto retry; } } dlog(LOG_ERR, "commit-destroy: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); tmp->c->stats.commit_fail++; } else { dlog(LOG_ERR, "commit-create: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); tmp->c->stats.commit_fail++; } } else { tmp->c->stats.commit_ok++; } }
static int data_cb(const struct nlmsghdr *nlh, void *data) { struct nf_conntrack *ct; struct data_cb_s * d = (struct data_cb_s*) data; struct sockaddr_in* ext4 = (struct sockaddr_in*) d->ext; ct = nfct_new(); if (ct == NULL) return MNL_CB_OK; nfct_nlmsg_parse(nlh, ct); if (data) { ext4->sin_addr.s_addr = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST); ext4->sin_port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST); } d->found = 1; nfct_destroy(ct); return MNL_CB_OK; }
static int test_nfct_cmp_api_single(struct nf_conntrack *ct1, struct nf_conntrack *ct2, int attr) { char data[256]; struct nfct_bitmask *b; int bit; if (attr_is_readonly(attr)) return 0; switch (attr) { case ATTR_SECMARK: /* obsolete */ return 0; /* FIXME: not implemented comparators: */ case ATTR_SNAT_IPV4: case ATTR_DNAT_IPV4: case ATTR_SNAT_PORT: case ATTR_DNAT_PORT: case ATTR_TCP_FLAGS_ORIG: case ATTR_TCP_FLAGS_REPL: case ATTR_TCP_MASK_ORIG: case ATTR_TCP_MASK_REPL: case ATTR_MASTER_IPV4_SRC: case ATTR_MASTER_IPV4_DST: case ATTR_MASTER_IPV6_SRC: case ATTR_MASTER_IPV6_DST: case ATTR_MASTER_PORT_SRC: case ATTR_MASTER_PORT_DST: case ATTR_MASTER_L3PROTO: case ATTR_MASTER_L4PROTO: case ATTR_ORIG_NAT_SEQ_CORRECTION_POS: case ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE: case ATTR_ORIG_NAT_SEQ_OFFSET_AFTER: case ATTR_REPL_NAT_SEQ_CORRECTION_POS: case ATTR_REPL_NAT_SEQ_OFFSET_BEFORE: case ATTR_REPL_NAT_SEQ_OFFSET_AFTER: case ATTR_SCTP_VTAG_ORIG: case ATTR_SCTP_VTAG_REPL: case ATTR_HELPER_NAME: case ATTR_DCCP_ROLE: case ATTR_DCCP_HANDSHAKE_SEQ: case ATTR_TCP_WSCALE_ORIG: case ATTR_TCP_WSCALE_REPL: case ATTR_HELPER_INFO: return 0; /* XXX */ default: break; } if (attr >= ATTR_SCTP_STATE) { nfct_set_attr_u8(ct1, ATTR_REPL_L4PROTO, IPPROTO_SCTP); nfct_set_attr_u8(ct1, ATTR_L4PROTO, IPPROTO_SCTP); } else if (attr >= ATTR_TCP_FLAGS_ORIG) { nfct_set_attr_u8(ct1, ATTR_REPL_L4PROTO, IPPROTO_TCP); nfct_set_attr_u8(ct1, ATTR_L4PROTO, IPPROTO_TCP); } else if (attr >= ATTR_ICMP_CODE) { nfct_set_attr_u8(ct1, ATTR_REPL_L4PROTO, IPPROTO_ICMP); nfct_set_attr_u8(ct1, ATTR_L4PROTO, IPPROTO_ICMP); } else if (attr >= ATTR_ORIG_PORT_SRC) { nfct_set_attr_u8(ct1, ATTR_REPL_L4PROTO, IPPROTO_TCP); nfct_set_attr_u8(ct1, ATTR_L4PROTO, IPPROTO_TCP); } nfct_copy(ct2, ct1, NFCT_CP_OVERRIDE); memset(data, 42, sizeof(data)); assert(nfct_attr_is_set(ct1, attr)); assert(nfct_attr_is_set(ct2, attr)); switch (attr) { case ATTR_CONNLABELS: case ATTR_CONNLABELS_MASK: b = (void *) nfct_get_attr(ct1, attr); assert(b); b = nfct_bitmask_clone(b); assert(b); bit = nfct_bitmask_maxbit(b); if (nfct_bitmask_test_bit(b, bit)) { nfct_bitmask_unset_bit(b, bit); assert(!nfct_bitmask_test_bit(b, bit)); } else { nfct_bitmask_set_bit(b, bit); assert(nfct_bitmask_test_bit(b, bit)); } assert(nfct_cmp(ct1, ct2, NFCT_CMP_ALL) == 1); nfct_set_attr(ct2, attr, b); break; case ATTR_HELPER_INFO: nfct_set_attr_l(ct2, attr, "test", 4); break; default: nfct_set_attr(ct2, attr, data); break; } if (nfct_cmp(ct1, ct2, NFCT_CMP_ALL) != 0) { fprintf(stderr, "nfct_cmp assert failure for attr %d\n", attr); fprintf(stderr, "%p, %p, %x, %x\n", nfct_get_attr(ct1, attr), nfct_get_attr(ct2, attr), nfct_get_attr_u32(ct1, attr), nfct_get_attr_u32(ct2, attr)); return -1; } if (nfct_cmp(ct1, ct2, NFCT_CMP_ALL|NFCT_CMP_STRICT) != 0) { fprintf(stderr, "nfct_cmp strict assert failure for attr %d\n", attr); return -1; } return 0; }
static int rpc_helper_cb(struct pkt_buff *pkt, uint32_t protoff, struct myct *myct, uint32_t ctinfo) { int dir = CTINFO2DIR(ctinfo); unsigned int offset = protoff, datalen; uint32_t *data, *port_ptr = NULL, xid; uint16_t port; uint8_t proto = nfct_get_attr_u8(myct->ct, ATTR_L4PROTO); enum msg_type rm_dir; struct rpc_info *rpc_info = myct->priv_data; union nfct_attr_grp_addr addr, daddr; struct nf_expect *exp = NULL; int ret = NF_ACCEPT; /* Until there's been traffic both ways, don't look into TCP packets. */ if (proto == IPPROTO_TCP && ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) { pr_debug("TCP RPC: Conntrackinfo = %u\n", ctinfo); return ret; } if (proto == IPPROTO_TCP) { struct tcphdr *th = (struct tcphdr *) (pktb_network_header(pkt) + protoff); offset += th->doff * 4; } else { offset += sizeof(struct udphdr); } /* Skip broken headers */ if (offset % 4) { pr_debug("RPC: broken header: offset %u%%4 != 0\n", offset); return ret; } /* Take into Record Fragment header */ if (proto == IPPROTO_TCP) offset += 4; datalen = pktb_len(pkt); data = (uint32_t *)(pktb_network_header(pkt) + offset); /* rpc_msg { * xid * direction * xdr_union { * call_body * reply_body * } * } */ /* Check minimal msg size: xid + direction */ if (datalen < OFFSET(offset, 2*4)) { pr_debug("RPC: too short packet: %u < %u\n", datalen, offset); return ret; } xid = IXDR_GET_INT32(data); rm_dir = IXDR_GET_INT32(data); /* Check direction */ if (!((rm_dir == CALL && dir == MYCT_DIR_ORIG) || (rm_dir == REPLY && dir == MYCT_DIR_REPL))) { pr_debug("RPC: rm_dir != dir %u != %u\n", rm_dir, dir); goto out; } if (rm_dir == CALL) { if (rpc_call(data, offset, datalen, rpc_info) < 0) goto out; rpc_info->xid = xid; return ret; } else { /* Check XID */ if (xid != rpc_info->xid) { pr_debug("RPC REPL: XID does not match: %u != %u\n", xid, rpc_info->xid); goto out; } if (rpc_reply(data, offset, datalen, rpc_info, &port_ptr) < 0) goto out; port = IXDR_GET_INT32(port_ptr); port = htons(port); /* We refer to the reverse direction ("!dir") tuples here, * because we're expecting something in the other direction. * Doesn't matter unless NAT is happening. */ cthelper_get_addr_dst(myct->ct, !dir, &daddr); cthelper_get_addr_src(myct->ct, !dir, &addr); exp = nfexp_new(); if (exp == NULL) goto out; if (cthelper_expect_init(exp, myct->ct, 0, &addr, &daddr, rpc_info->pm_prot, NULL, &port, NF_CT_EXPECT_PERMANENT)) { pr_debug("RPC: failed to init expectation\n"); goto out_exp; } /* Now, NAT might want to mangle the packet, and register the * (possibly changed) expectation itself. */ if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { ret = nf_nat_rpc(pkt, dir, exp, rpc_info->pm_prot, port_ptr); goto out_exp; } /* Can't expect this? Best to drop packet now. */ if (cthelper_add_expect(exp) < 0) { pr_debug("RPC: cannot add expectation: %s\n", strerror(errno)); ret = NF_DROP; } } out_exp: nfexp_destroy(exp); out: rpc_info->xid = 0; return ret; }
void print_verbose(struct nf_conntrack *ct, enum nf_conntrack_msg_type type, char *proto_str) { int ret = 0, size = 0, offset = 0, len = BUF_LEN; char buf[BUF_LEN]; ret = __snprintf_start_log(buf, len, "DEBUG"); BUFFER_SIZE(ret, size, len, offset); switch(type) { case NFCT_T_NEW: ret = snprintf(buf+offset, len, " NEW"); BUFFER_SIZE(ret, size, len, offset); break; case NFCT_T_DESTROY: ret = snprintf(buf+offset, len, " DESTROY"); BUFFER_SIZE(ret, size, len, offset); break; default: break; } ret = snprintf(buf+offset, len, " id=%u", nfct_get_attr_u32(ct,ATTR_ID)); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " proto=%s", proto_str); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " orig-src=%s", net2addr(nfct_get_attr_u32(ct,ATTR_ORIG_IPV4_SRC))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " orig-dst=%s", net2addr(nfct_get_attr_u32(ct,ATTR_ORIG_IPV4_DST))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " orig-sport=%d", ntohs(nfct_get_attr_u16(ct,ATTR_ORIG_PORT_SRC))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " orig-dport=%d", ntohs(nfct_get_attr_u16(ct,ATTR_ORIG_PORT_DST))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " repl-src=%s", net2addr(nfct_get_attr_u32(ct,ATTR_REPL_IPV4_SRC))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " repl-dst=%s", net2addr(nfct_get_attr_u32(ct,ATTR_REPL_IPV4_DST))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " repl-sport=%d", ntohs(nfct_get_attr_u16(ct,ATTR_REPL_PORT_SRC))); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, " repl-dport=%d", ntohs(nfct_get_attr_u16(ct,ATTR_REPL_PORT_DST))); BUFFER_SIZE(ret, size, len, offset); buf[size+1 > len ? len-1 : size] = '\0'; write_msg(LOG_INFO, buf); }
int nl_update_conntrack(struct nfct_handle *h, const struct nf_conntrack *orig, int timeout) { int ret; struct nf_conntrack *ct; ct = nfct_clone(orig); if (ct == NULL) return -1; if (timeout > 0) nfct_set_attr_u32(ct, ATTR_TIMEOUT, timeout); /* unset NAT info, otherwise we hit error */ nfct_attr_unset(ct, ATTR_SNAT_IPV4); nfct_attr_unset(ct, ATTR_DNAT_IPV4); nfct_attr_unset(ct, ATTR_SNAT_PORT); nfct_attr_unset(ct, ATTR_DNAT_PORT); if (nfct_attr_is_set(ct, ATTR_STATUS)) { uint32_t status = nfct_get_attr_u32(ct, ATTR_STATUS); status &= ~IPS_NAT_MASK; nfct_set_attr_u32(ct, ATTR_STATUS, status); } /* we have to unset the helper to avoid EBUSY in reset timers */ if (nfct_attr_is_set(ct, ATTR_HELPER_NAME)) nfct_attr_unset(ct, ATTR_HELPER_NAME); /* we hit error if we try to update the master conntrack */ if (ct_is_related(ct)) { nfct_attr_unset(ct, ATTR_MASTER_L3PROTO); nfct_attr_unset(ct, ATTR_MASTER_L4PROTO); nfct_attr_unset(ct, ATTR_MASTER_IPV4_SRC); nfct_attr_unset(ct, ATTR_MASTER_IPV4_DST); nfct_attr_unset(ct, ATTR_MASTER_IPV6_SRC); nfct_attr_unset(ct, ATTR_MASTER_IPV6_DST); nfct_attr_unset(ct, ATTR_MASTER_PORT_SRC); nfct_attr_unset(ct, ATTR_MASTER_PORT_DST); } /* disable TCP window tracking for recovered connections if required */ if (nfct_attr_is_set(ct, ATTR_TCP_STATE)) { uint8_t flags = IP_CT_TCP_FLAG_SACK_PERM; if (!CONFIG(sync).tcp_window_tracking) flags |= IP_CT_TCP_FLAG_BE_LIBERAL; else flags |= IP_CT_TCP_FLAG_WINDOW_SCALE; /* FIXME: workaround, we should send TCP flags in updates */ if (nfct_get_attr_u8(ct, ATTR_TCP_STATE) >= TCP_CONNTRACK_TIME_WAIT) { flags |= IP_CT_TCP_FLAG_CLOSE_INIT; } nfct_set_attr_u8(ct, ATTR_TCP_FLAGS_ORIG, flags); nfct_set_attr_u8(ct, ATTR_TCP_MASK_ORIG, flags); nfct_set_attr_u8(ct, ATTR_TCP_FLAGS_REPL, flags); nfct_set_attr_u8(ct, ATTR_TCP_MASK_REPL, flags); } ret = nfct_query(h, NFCT_Q_UPDATE, ct); nfct_destroy(ct); return ret; }