| /* |
| * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org> |
| * (C) 2011 by Vyatta Inc. <http://www.vyatta.com> |
| * |
| * 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 <string.h> |
| #include <libnetfilter_conntrack/libnetfilter_conntrack.h> |
| #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); |
| } |