/* * (C) 2006-2011 by Pablo Neira Ayuso * (C) 2011 by Vyatta Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "network.h" #include "conntrackd.h" static inline void * put_header(struct nethdr *n, int attr, size_t len) { struct netattr *nta = NETHDR_TAIL(n); int total_size = NTA_ALIGN(NTA_LENGTH(len)); int attr_size = NTA_LENGTH(len); n->len += total_size; nta->nta_attr = htons(attr); nta->nta_len = htons(attr_size); memset((unsigned char *)nta + attr_size, 0, total_size - attr_size); return NTA_DATA(nta); } static inline void addattr(struct nethdr *n, int attr, const void *data, size_t len) { void *ptr = put_header(n, attr, len); memcpy(ptr, data, len); } static inline void ct_build_u8(const struct nf_conntrack *ct, int a, struct nethdr *n, int b) { void *ptr = put_header(n, b, sizeof(uint8_t)); memcpy(ptr, nfct_get_attr(ct, a), sizeof(uint8_t)); } static inline void ct_build_u16(const struct nf_conntrack *ct, int a, struct nethdr *n, int b) { uint16_t data = nfct_get_attr_u16(ct, a); data = htons(data); addattr(n, b, &data, sizeof(uint16_t)); } 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 inline void ct_build_u128(const struct nf_conntrack *ct, int a, struct nethdr *n, int b) { const char *data = nfct_get_attr(ct, a); addattr(n, b, data, sizeof(uint32_t) * 4); } static inline void ct_build_str(const struct nf_conntrack *ct, int a, struct nethdr *n, int b) { const void *data = nfct_get_attr(ct, a); addattr(n, b, data, strlen(data)+1); } static inline void ct_build_group(const struct nf_conntrack *ct, int a, struct nethdr *n, int b, int size) { void *ptr = put_header(n, b, size); nfct_get_attr_grp(ct, a, ptr); } static inline void ct_build_natseqadj(const struct nf_conntrack *ct, struct nethdr *n) { struct nta_attr_natseqadj data = { .orig_seq_correction_pos = htonl(nfct_get_attr_u32(ct, ATTR_ORIG_NAT_SEQ_CORRECTION_POS)), .orig_seq_offset_before = htonl(nfct_get_attr_u32(ct, ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE)), .orig_seq_offset_after = htonl(nfct_get_attr_u32(ct, ATTR_ORIG_NAT_SEQ_OFFSET_AFTER)), .repl_seq_correction_pos = htonl(nfct_get_attr_u32(ct, ATTR_REPL_NAT_SEQ_CORRECTION_POS)), .repl_seq_offset_before = htonl(nfct_get_attr_u32(ct, ATTR_REPL_NAT_SEQ_OFFSET_BEFORE)), .repl_seq_offset_after = htonl(nfct_get_attr_u32(ct, ATTR_REPL_NAT_SEQ_OFFSET_AFTER)) }; addattr(n, NTA_NAT_SEQ_ADJ, &data, sizeof(struct nta_attr_natseqadj)); } static enum nf_conntrack_attr nat_type[] = { ATTR_ORIG_NAT_SEQ_CORRECTION_POS, ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE, ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, ATTR_REPL_NAT_SEQ_CORRECTION_POS, ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, ATTR_REPL_NAT_SEQ_OFFSET_AFTER }; /* ICMP, UDP and TCP are always loaded with nf_conntrack_ipv4 */ static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n) { if (!nfct_attr_is_set(ct, ATTR_TCP_STATE)) return; ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_TCP_STATE, n, NTA_TCP_STATE); if (CONFIG(sync).tcp_window_tracking) { ct_build_u8(ct, ATTR_TCP_WSCALE_ORIG, n, NTA_TCP_WSCALE_ORIG); ct_build_u8(ct, ATTR_TCP_WSCALE_REPL, n, NTA_TCP_WSCALE_REPL); } } static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n) { /* SCTP is optional, make sure nf_conntrack_sctp is loaded */ if (!nfct_attr_is_set(ct, ATTR_SCTP_STATE)) return; ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_SCTP_STATE, n, NTA_SCTP_STATE); ct_build_u32(ct, ATTR_SCTP_VTAG_ORIG, n, NTA_SCTP_VTAG_ORIG); ct_build_u32(ct, ATTR_SCTP_VTAG_REPL, n, NTA_SCTP_VTAG_REPL); } static void build_l4proto_dccp(const struct nf_conntrack *ct, struct nethdr *n) { /* DCCP is optional, make sure nf_conntrack_dccp is loaded */ if (!nfct_attr_is_set(ct, ATTR_DCCP_STATE)) return; ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_DCCP_STATE, n, NTA_DCCP_STATE); ct_build_u8(ct, ATTR_DCCP_ROLE, n, NTA_DCCP_ROLE); } static void build_l4proto_icmp(const struct nf_conntrack *ct, struct nethdr *n) { /* This is also used by ICMPv6 and nf_conntrack_ipv6 is optional */ if (!nfct_attr_is_set(ct, ATTR_ICMP_TYPE)) return; ct_build_u8(ct, ATTR_ICMP_TYPE, n, NTA_ICMP_TYPE); ct_build_u8(ct, ATTR_ICMP_CODE, n, NTA_ICMP_CODE); ct_build_u16(ct, ATTR_ICMP_ID, n, NTA_ICMP_ID); } static void build_l4proto_udp(const struct nf_conntrack *ct, struct nethdr *n) { ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, sizeof(struct nfct_attr_grp_port)); } static void ct_build_clabel(const struct nf_conntrack *ct, struct nethdr *n) { const struct nfct_bitmask *b; uint32_t *words; unsigned int wordcount, i, maxbit; if (!nfct_attr_is_set(ct, ATTR_CONNLABELS)) return; b = nfct_get_attr(ct, ATTR_CONNLABELS); maxbit = nfct_bitmask_maxbit(b); for (i=0; i <= maxbit; i++) { if (nfct_bitmask_test_bit(b, i)) break; } if (i > maxbit) return; wordcount = (nfct_bitmask_maxbit(b) / 32) + 1; words = put_header(n, NTA_LABELS, wordcount * sizeof(*words)); for (i=0; i < wordcount; i++) { int bit = 31; uint32_t tmp = 0; do { if (nfct_bitmask_test_bit(b, (32 * i) + bit)) tmp |= (1 << bit); } while (--bit >= 0); words[i] = htonl(tmp); } } #ifndef IPPROTO_DCCP #define IPPROTO_DCCP 33 #endif static struct build_l4proto { void (*build)(const struct nf_conntrack *, struct nethdr *n); } l4proto_fcn[IPPROTO_MAX] = { [IPPROTO_TCP] = { .build = build_l4proto_tcp }, [IPPROTO_SCTP] = { .build = build_l4proto_sctp }, [IPPROTO_DCCP] = { .build = build_l4proto_dccp }, [IPPROTO_ICMP] = { .build = build_l4proto_icmp }, [IPPROTO_ICMPV6] = { .build = build_l4proto_icmp }, [IPPROTO_UDP] = { .build = build_l4proto_udp }, }; void ct2msg(const struct nf_conntrack *ct, struct nethdr *n) { uint8_t l4proto = nfct_get_attr_u8(ct, ATTR_L4PROTO); if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_IPV4, sizeof(struct nfct_attr_grp_ipv4)); } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_IPV6, sizeof(struct nfct_attr_grp_ipv6)); } ct_build_u32(ct, ATTR_STATUS, n, NTA_STATUS); ct_build_u8(ct, ATTR_L4PROTO, n, NTA_L4PROTO); if (l4proto_fcn[l4proto].build) l4proto_fcn[l4proto].build(ct, n); if (!CONFIG(commit_timeout) && nfct_attr_is_set(ct, ATTR_TIMEOUT)) ct_build_u32(ct, ATTR_TIMEOUT, n, NTA_TIMEOUT); if (nfct_attr_is_set(ct, ATTR_MARK)) ct_build_u32(ct, ATTR_MARK, n, NTA_MARK); /* setup the master conntrack */ if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_IPV4)) { ct_build_group(ct, ATTR_GRP_MASTER_IPV4, n, NTA_MASTER_IPV4, sizeof(struct nfct_attr_grp_ipv4)); ct_build_u8(ct, ATTR_MASTER_L4PROTO, n, NTA_MASTER_L4PROTO); if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_PORT)) { ct_build_group(ct, ATTR_GRP_MASTER_PORT, n, NTA_MASTER_PORT, sizeof(struct nfct_attr_grp_port)); } } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_IPV6)) { ct_build_group(ct, ATTR_GRP_MASTER_IPV6, n, NTA_MASTER_IPV6, sizeof(struct nfct_attr_grp_ipv6)); ct_build_u8(ct, ATTR_MASTER_L4PROTO, n, NTA_MASTER_L4PROTO); if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_PORT)) { ct_build_group(ct, ATTR_GRP_MASTER_PORT, n, NTA_MASTER_PORT, sizeof(struct nfct_attr_grp_port)); } } /* NAT */ switch (nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO)) { case AF_INET: if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT)) ct_build_u32(ct, ATTR_REPL_IPV4_DST, n, NTA_SNAT_IPV4); if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT)) ct_build_u32(ct, ATTR_REPL_IPV4_SRC, n, NTA_DNAT_IPV4); break; case AF_INET6: if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT)) { ct_build_u128(ct, ATTR_REPL_IPV6_DST, n, NTA_SNAT_IPV6); } if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT)) { ct_build_u128(ct, ATTR_REPL_IPV6_SRC, n, NTA_DNAT_IPV6); } break; default: break; } if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT)) ct_build_u16(ct, ATTR_REPL_PORT_DST, n, NTA_SPAT_PORT); if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT)) ct_build_u16(ct, ATTR_REPL_PORT_SRC, n, NTA_DPAT_PORT); /* NAT sequence adjustment */ if (nfct_attr_is_set_array(ct, nat_type, 6)) ct_build_natseqadj(ct, n); if (nfct_attr_is_set(ct, ATTR_HELPER_NAME)) ct_build_str(ct, ATTR_HELPER_NAME, n, NTA_HELPER_NAME); if (nfct_attr_is_set(ct, ATTR_CONNLABELS)) ct_build_clabel(ct, n); } static void exp_build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n, int a) { ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a, sizeof(struct nfct_attr_grp_port)); } static void exp_build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n, int a) { ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a, sizeof(struct nfct_attr_grp_port)); } static void exp_build_l4proto_dccp(const struct nf_conntrack *ct, struct nethdr *n, int a) { ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a, sizeof(struct nfct_attr_grp_port)); } static void exp_build_l4proto_udp(const struct nf_conntrack *ct, struct nethdr *n, int a) { ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a, sizeof(struct nfct_attr_grp_port)); } static struct exp_build_l4proto { void (*build)(const struct nf_conntrack *, struct nethdr *n, int a); } exp_l4proto_fcn[IPPROTO_MAX] = { [IPPROTO_TCP] = { .build = exp_build_l4proto_tcp }, [IPPROTO_SCTP] = { .build = exp_build_l4proto_sctp }, [IPPROTO_DCCP] = { .build = exp_build_l4proto_dccp }, [IPPROTO_UDP] = { .build = exp_build_l4proto_udp }, }; static inline void exp_build_u32(const struct nf_expect *exp, int a, struct nethdr *n, int b) { uint32_t data = nfexp_get_attr_u32(exp, a); data = htonl(data); addattr(n, b, &data, sizeof(uint32_t)); } static inline void exp_build_str(const struct nf_expect *exp, int a, struct nethdr *n, int b) { const char *data = nfexp_get_attr(exp, a); addattr(n, b, data, strlen(data)+1); } void exp2msg(const struct nf_expect *exp, struct nethdr *n) { const struct nf_conntrack *ct = nfexp_get_attr(exp, ATTR_EXP_MASTER); uint8_t l4proto = nfct_get_attr_u8(ct, ATTR_L4PROTO); /* master conntrack for this expectation. */ if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_EXP_MASTER_IPV4, sizeof(struct nfct_attr_grp_ipv4)); } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_EXP_MASTER_IPV6, sizeof(struct nfct_attr_grp_ipv6)); } ct_build_u8(ct, ATTR_L4PROTO, n, NTA_EXP_MASTER_L4PROTO); if (exp_l4proto_fcn[l4proto].build) exp_l4proto_fcn[l4proto].build(ct, n, NTA_EXP_MASTER_PORT); /* the expectation itself. */ ct = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_EXP_EXPECT_IPV4, sizeof(struct nfct_attr_grp_ipv4)); } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_EXP_EXPECT_IPV6, sizeof(struct nfct_attr_grp_ipv6)); } ct_build_u8(ct, ATTR_L4PROTO, n, NTA_EXP_EXPECT_L4PROTO); if (exp_l4proto_fcn[l4proto].build) exp_l4proto_fcn[l4proto].build(ct, n, NTA_EXP_EXPECT_PORT); /* mask for the expectation. */ ct = nfexp_get_attr(exp, ATTR_EXP_MASK); if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_EXP_MASK_IPV4, sizeof(struct nfct_attr_grp_ipv4)); } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_EXP_MASK_IPV6, sizeof(struct nfct_attr_grp_ipv6)); } ct_build_u8(ct, ATTR_L4PROTO, n, NTA_EXP_MASK_L4PROTO); if (exp_l4proto_fcn[l4proto].build) exp_l4proto_fcn[l4proto].build(ct, n, NTA_EXP_MASK_PORT); if (!CONFIG(commit_timeout) && nfexp_attr_is_set(exp, ATTR_EXP_TIMEOUT)) exp_build_u32(exp, ATTR_EXP_TIMEOUT, n, NTA_EXP_TIMEOUT); exp_build_u32(exp, ATTR_EXP_FLAGS, n, NTA_EXP_FLAGS); if (nfexp_attr_is_set(exp, ATTR_EXP_CLASS)) exp_build_u32(exp, ATTR_EXP_CLASS, n, NTA_EXP_CLASS); /* include NAT information, if any. */ ct = nfexp_get_attr(exp, ATTR_EXP_NAT_TUPLE); if (ct != NULL) { if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) { ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_EXP_NAT_IPV4, sizeof(struct nfct_attr_grp_ipv4)); } ct_build_u8(ct, ATTR_L4PROTO, n, NTA_EXP_NAT_L4PROTO); if (exp_l4proto_fcn[l4proto].build) exp_l4proto_fcn[l4proto].build(ct, n, NTA_EXP_NAT_PORT); exp_build_u32(exp, ATTR_EXP_NAT_DIR, n, NTA_EXP_NAT_DIR); } if (nfexp_attr_is_set(exp, ATTR_EXP_HELPER_NAME)) exp_build_str(exp, ATTR_EXP_HELPER_NAME, n, NTA_EXP_HELPER_NAME); if (nfexp_attr_is_set(exp, ATTR_EXP_FN)) exp_build_str(exp, ATTR_EXP_FN, n, NTA_EXP_FN); }