This patch adds the capability to attach expectations via nfnetlink_queue. This is required by conntrack helpers that trigger expectations based on the first packet seen like the TFTP and the DHCPv6 user-space helpers.
Unified · Split
| @@ -325,6 +325,8 @@ struct nfq_ct_hook { · 324 unmodified lines | |||
| 325 | size_t (*build_size)(const struct nf_conn *ct); | 325 | size_t (*build_size)(const struct nf_conn *ct); |
| 326 | int (*build)(struct sk_buff *skb, struct nf_conn *ct); | 326 | int (*build)(struct sk_buff *skb, struct nf_conn *ct); |
| 327 | int (*parse)(const struct nlattr *attr, struct nf_conn *ct); | 327 | int (*parse)(const struct nlattr *attr, struct nf_conn *ct); |
| 328 | int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, | ||
| 329 | u32 portid, u32 report); | ||
| 328 | }; | 330 | }; |
| 329 | extern struct nfq_ct_hook __rcu *nfq_ct_hook; | 331 | extern struct nfq_ct_hook __rcu *nfq_ct_hook; |
| 330 | 332 | ||
| @@ -15,6 +15,8 @@ int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, · 14 unmodified lines | |||
| 15 | enum ip_conntrack_info ctinfo); | 15 | enum ip_conntrack_info ctinfo); |
| 16 | void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, | 16 | void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, |
| 17 | enum ip_conntrack_info ctinfo, int diff); | 17 | enum ip_conntrack_info ctinfo, int diff); |
| 18 | int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, | ||
| 19 | u32 portid, u32 report); | ||
| 18 | #else | 20 | #else |
| 19 | inline struct nf_conn * | 21 | inline struct nf_conn * |
| 20 | nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) | 22 | nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) |
| @@ -39,5 +41,11 @@ inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, · 18 unmodified lines | |||
| 39 | enum ip_conntrack_info ctinfo, int diff) | 41 | enum ip_conntrack_info ctinfo, int diff) |
| 40 | { | 42 | { |
| 41 | } | 43 | } |
| 44 | |||
| 45 | inline int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, | ||
| 46 | u32 portid, u32 report) | ||
| 47 | { | ||
| 48 | return 0; | ||
| 49 | } | ||
| 42 | #endif /* NF_CONNTRACK */ | 50 | #endif /* NF_CONNTRACK */ |
| 43 | #endif | 51 | #endif |
| @@ -46,6 +46,7 @@ enum nfqnl_attr_type { · 45 unmodified lines | |||
| 46 | NFQA_CT_INFO, /* enum ip_conntrack_info */ | 46 | NFQA_CT_INFO, /* enum ip_conntrack_info */ |
| 47 | NFQA_CAP_LEN, /* __u32 length of captured packet */ | 47 | NFQA_CAP_LEN, /* __u32 length of captured packet */ |
| 48 | NFQA_SKB_INFO, /* __u32 skb meta information */ | 48 | NFQA_SKB_INFO, /* __u32 skb meta information */ |
| 49 | NFQA_EXP, /* nf_conntrack_netlink.h */ | ||
| 49 | 50 | ||
| 50 | __NFQA_MAX | 51 | __NFQA_MAX |
| 51 | }; | 52 | }; |
| @@ -1987,6 +1987,27 @@ out: · 1986 unmodified lines | |||
| 1987 | return err == -EAGAIN ? -ENOBUFS : err; | 1987 | return err == -EAGAIN ? -ENOBUFS : err; |
| 1988 | } | 1988 | } |
| 1989 | 1989 | ||
| 1990 | static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { | ||
| 1991 | [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, | ||
| 1992 | [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, | ||
| 1993 | [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, | ||
| 1994 | [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, | ||
| 1995 | [CTA_EXPECT_ID] = { .type = NLA_U32 }, | ||
| 1996 | [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING, | ||
| 1997 | .len = NF_CT_HELPER_NAME_LEN - 1 }, | ||
| 1998 | [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, | ||
| 1999 | [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, | ||
| 2000 | [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, | ||
| 2001 | [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, | ||
| 2002 | [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING }, | ||
| 2003 | }; | ||
| 2004 | |||
| 2005 | static struct nf_conntrack_expect * | ||
| 2006 | ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, | ||
| 2007 | struct nf_conntrack_helper *helper, | ||
| 2008 | struct nf_conntrack_tuple *tuple, | ||
| 2009 | struct nf_conntrack_tuple *mask); | ||
| 2010 | |||
| 1990 | #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT | 2011 | #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT |
| 1991 | static size_t | 2012 | static size_t |
| 1992 | ctnetlink_nfqueue_build_size(const struct nf_conn *ct) | 2013 | ctnetlink_nfqueue_build_size(const struct nf_conn *ct) |
| @@ -2127,10 +2148,69 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) · 134 unmodified lines | |||
| 2127 | return ret; | 2148 | return ret; |
| 2128 | } | 2149 | } |
| 2129 | 2150 | ||
| 2151 | static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda, | ||
| 2152 | const struct nf_conn *ct, | ||
| 2153 | struct nf_conntrack_tuple *tuple, | ||
| 2154 | struct nf_conntrack_tuple *mask) | ||
| 2155 | { | ||
| 2156 | int err; | ||
| 2157 | |||
| 2158 | err = ctnetlink_parse_tuple(cda, tuple, CTA_EXPECT_TUPLE, | ||
| 2159 | nf_ct_l3num(ct)); | ||
| 2160 | if (err < 0) | ||
| 2161 | return err; | ||
| 2162 | |||
| 2163 | return ctnetlink_parse_tuple(cda, mask, CTA_EXPECT_MASK, | ||
| 2164 | nf_ct_l3num(ct)); | ||
| 2165 | } | ||
| 2166 | |||
| 2167 | static int | ||
| 2168 | ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, | ||
| 2169 | u32 portid, u32 report) | ||
| 2170 | { | ||
| 2171 | struct nlattr *cda[CTA_EXPECT_MAX+1]; | ||
| 2172 | struct nf_conntrack_tuple tuple, mask; | ||
| 2173 | struct nf_conntrack_helper *helper; | ||
| 2174 | struct nf_conntrack_expect *exp; | ||
| 2175 | int err; | ||
| 2176 | |||
| 2177 | err = nla_parse_nested(cda, CTA_EXPECT_MAX, attr, exp_nla_policy); | ||
| 2178 | if (err < 0) | ||
| 2179 | return err; | ||
| 2180 | |||
| 2181 | err = ctnetlink_nfqueue_exp_parse((const struct nlattr * const *)cda, | ||
| 2182 | ct, &tuple, &mask); | ||
| 2183 | if (err < 0) | ||
| 2184 | return err; | ||
| 2185 | |||
| 2186 | if (cda[CTA_EXPECT_HELP_NAME]) { | ||
| 2187 | const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); | ||
| 2188 | |||
| 2189 | helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), | ||
| 2190 | nf_ct_protonum(ct)); | ||
| 2191 | if (helper == NULL) | ||
| 2192 | return -EOPNOTSUPP; | ||
| 2193 | } | ||
| 2194 | |||
| 2195 | exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, | ||
| 2196 | helper, &tuple, &mask); | ||
| 2197 | if (IS_ERR(exp)) | ||
| 2198 | return PTR_ERR(exp); | ||
| 2199 | |||
| 2200 | err = nf_ct_expect_related_report(exp, portid, report); | ||
| 2201 | if (err < 0) { | ||
| 2202 | nf_ct_expect_put(exp); | ||
| 2203 | return err; | ||
| 2204 | } | ||
| 2205 | |||
| 2206 | return 0; | ||
| 2207 | } | ||
| 2208 | |||
| 2130 | static struct nfq_ct_hook ctnetlink_nfqueue_hook = { | 2209 | static struct nfq_ct_hook ctnetlink_nfqueue_hook = { |
| 2131 | .build_size = ctnetlink_nfqueue_build_size, | 2210 | .build_size = ctnetlink_nfqueue_build_size, |
| 2132 | .build = ctnetlink_nfqueue_build, | 2211 | .build = ctnetlink_nfqueue_build, |
| 2133 | .parse = ctnetlink_nfqueue_parse, | 2212 | .parse = ctnetlink_nfqueue_parse, |
| 2213 | .attach_expect = ctnetlink_nfqueue_attach_expect, | ||
| 2134 | }; | 2214 | }; |
| 2135 | #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ | 2215 | #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ |
| 2136 | 2216 | ||
| @@ -2498,21 +2578,6 @@ static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb, · 361 unmodified lines | |||
| 2498 | return err; | 2578 | return err; |
| 2499 | } | 2579 | } |
| 2500 | 2580 | ||
| 2501 | static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { | ||
| 2502 | [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, | ||
| 2503 | [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, | ||
| 2504 | [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, | ||
| 2505 | [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, | ||
| 2506 | [CTA_EXPECT_ID] = { .type = NLA_U32 }, | ||
| 2507 | [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING, | ||
| 2508 | .len = NF_CT_HELPER_NAME_LEN - 1 }, | ||
| 2509 | [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, | ||
| 2510 | [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, | ||
| 2511 | [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, | ||
| 2512 | [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, | ||
| 2513 | [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING }, | ||
| 2514 | }; | ||
| 2515 | |||
| 2516 | static int | 2581 | static int |
| 2517 | ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, | 2582 | ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, |
| 2518 | const struct nlmsghdr *nlh, | 2583 | const struct nlmsghdr *nlh, |
| @@ -859,6 +859,7 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { · 858 unmodified lines | |||
| 859 | [NFQA_MARK] = { .type = NLA_U32 }, | 859 | [NFQA_MARK] = { .type = NLA_U32 }, |
| 860 | [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, | 860 | [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, |
| 861 | [NFQA_CT] = { .type = NLA_UNSPEC }, | 861 | [NFQA_CT] = { .type = NLA_UNSPEC }, |
| 862 | [NFQA_EXP] = { .type = NLA_UNSPEC }, | ||
| 862 | }; | 863 | }; |
| 863 | 864 | ||
| 864 | static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { | 865 | static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { |
| @@ -987,8 +988,14 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, · 122 unmodified lines | |||
| 987 | if (entry == NULL) | 988 | if (entry == NULL) |
| 988 | return -ENOENT; | 989 | return -ENOENT; |
| 989 | 990 | ||
| 990 | if (nfqa[NFQA_CT]) | 991 | if (nfqa[NFQA_CT]) { |
| 991 | ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); | 992 | ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); |
| 993 | if (ct && nfqa[NFQA_EXP]) { | ||
| 994 | nfqnl_attach_expect(ct, nfqa[NFQA_EXP], | ||
| 995 | NETLINK_CB(skb).portid, | ||
| 996 | nlmsg_report(nlh)); | ||
| 997 | } | ||
| 998 | } | ||
| 992 | 999 | ||
| 993 | if (nfqa[NFQA_PAYLOAD]) { | 1000 | if (nfqa[NFQA_PAYLOAD]) { |
| 994 | u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); | 1001 | u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); |
| @@ -96,3 +96,18 @@ void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, · 95 unmodified lines | |||
| 96 | if ((ct->status & IPS_NAT_MASK) && diff) | 96 | if ((ct->status & IPS_NAT_MASK) && diff) |
| 97 | nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); | 97 | nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); |
| 98 | } | 98 | } |
| 99 | |||
| 100 | int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, | ||
| 101 | u32 portid, u32 report) | ||
| 102 | { | ||
| 103 | struct nfq_ct_hook *nfq_ct; | ||
| 104 | |||
| 105 | if (nf_ct_is_untracked(ct)) | ||
| 106 | return 0; | ||
| 107 | |||
| 108 | nfq_ct = rcu_dereference(nfq_ct_hook); | ||
| 109 | if (nfq_ct == NULL) | ||
| 110 | return -EOPNOTSUPP; | ||
| 111 | |||
| 112 | return nfq_ct->attach_expect(attr, ct, portid, report); | ||
| 113 | } | ||