nft_ct.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. * Development of this code funded by Astaro AG (http://www.astaro.com/)
  9. */
  10. #include <linux/kernel.h>
  11. #include <linux/init.h>
  12. #include <linux/module.h>
  13. #include <linux/netlink.h>
  14. #include <linux/netfilter.h>
  15. #include <linux/netfilter/nf_tables.h>
  16. #include <net/netfilter/nf_tables.h>
  17. #include <net/netfilter/nf_conntrack.h>
  18. #include <net/netfilter/nf_conntrack_tuple.h>
  19. #include <net/netfilter/nf_conntrack_helper.h>
  20. #include <net/netfilter/nf_conntrack_ecache.h>
  21. #include <net/netfilter/nf_conntrack_labels.h>
  22. struct nft_ct {
  23. enum nft_ct_keys key:8;
  24. enum ip_conntrack_dir dir:8;
  25. union {
  26. enum nft_registers dreg:8;
  27. enum nft_registers sreg:8;
  28. };
  29. };
  30. static void nft_ct_get_eval(const struct nft_expr *expr,
  31. struct nft_data data[NFT_REG_MAX + 1],
  32. const struct nft_pktinfo *pkt)
  33. {
  34. const struct nft_ct *priv = nft_expr_priv(expr);
  35. struct nft_data *dest = &data[priv->dreg];
  36. enum ip_conntrack_info ctinfo;
  37. const struct nf_conn *ct;
  38. const struct nf_conn_help *help;
  39. const struct nf_conntrack_tuple *tuple;
  40. const struct nf_conntrack_helper *helper;
  41. long diff;
  42. unsigned int state;
  43. ct = nf_ct_get(pkt->skb, &ctinfo);
  44. switch (priv->key) {
  45. case NFT_CT_STATE:
  46. if (ct == NULL)
  47. state = NF_CT_STATE_INVALID_BIT;
  48. else if (nf_ct_is_untracked(ct))
  49. state = NF_CT_STATE_UNTRACKED_BIT;
  50. else
  51. state = NF_CT_STATE_BIT(ctinfo);
  52. dest->data[0] = state;
  53. return;
  54. }
  55. if (ct == NULL)
  56. goto err;
  57. switch (priv->key) {
  58. case NFT_CT_DIRECTION:
  59. dest->data[0] = CTINFO2DIR(ctinfo);
  60. return;
  61. case NFT_CT_STATUS:
  62. dest->data[0] = ct->status;
  63. return;
  64. #ifdef CONFIG_NF_CONNTRACK_MARK
  65. case NFT_CT_MARK:
  66. dest->data[0] = ct->mark;
  67. return;
  68. #endif
  69. #ifdef CONFIG_NF_CONNTRACK_SECMARK
  70. case NFT_CT_SECMARK:
  71. dest->data[0] = ct->secmark;
  72. return;
  73. #endif
  74. case NFT_CT_EXPIRATION:
  75. diff = (long)jiffies - (long)ct->timeout.expires;
  76. if (diff < 0)
  77. diff = 0;
  78. dest->data[0] = jiffies_to_msecs(diff);
  79. return;
  80. case NFT_CT_HELPER:
  81. if (ct->master == NULL)
  82. goto err;
  83. help = nfct_help(ct->master);
  84. if (help == NULL)
  85. goto err;
  86. helper = rcu_dereference(help->helper);
  87. if (helper == NULL)
  88. goto err;
  89. if (strlen(helper->name) >= sizeof(dest->data))
  90. goto err;
  91. strncpy((char *)dest->data, helper->name, sizeof(dest->data));
  92. return;
  93. #ifdef CONFIG_NF_CONNTRACK_LABELS
  94. case NFT_CT_LABELS: {
  95. struct nf_conn_labels *labels = nf_ct_labels_find(ct);
  96. unsigned int size;
  97. if (!labels) {
  98. memset(dest->data, 0, sizeof(dest->data));
  99. return;
  100. }
  101. BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > sizeof(dest->data));
  102. size = labels->words * sizeof(long);
  103. memcpy(dest->data, labels->bits, size);
  104. if (size < sizeof(dest->data))
  105. memset(((char *) dest->data) + size, 0,
  106. sizeof(dest->data) - size);
  107. return;
  108. }
  109. #endif
  110. }
  111. tuple = &ct->tuplehash[priv->dir].tuple;
  112. switch (priv->key) {
  113. case NFT_CT_L3PROTOCOL:
  114. dest->data[0] = nf_ct_l3num(ct);
  115. return;
  116. case NFT_CT_SRC:
  117. memcpy(dest->data, tuple->src.u3.all,
  118. nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
  119. return;
  120. case NFT_CT_DST:
  121. memcpy(dest->data, tuple->dst.u3.all,
  122. nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
  123. return;
  124. case NFT_CT_PROTOCOL:
  125. dest->data[0] = nf_ct_protonum(ct);
  126. return;
  127. case NFT_CT_PROTO_SRC:
  128. dest->data[0] = (__force __u16)tuple->src.u.all;
  129. return;
  130. case NFT_CT_PROTO_DST:
  131. dest->data[0] = (__force __u16)tuple->dst.u.all;
  132. return;
  133. }
  134. return;
  135. err:
  136. data[NFT_REG_VERDICT].verdict = NFT_BREAK;
  137. }
  138. static void nft_ct_set_eval(const struct nft_expr *expr,
  139. struct nft_data data[NFT_REG_MAX + 1],
  140. const struct nft_pktinfo *pkt)
  141. {
  142. const struct nft_ct *priv = nft_expr_priv(expr);
  143. struct sk_buff *skb = pkt->skb;
  144. #ifdef CONFIG_NF_CONNTRACK_MARK
  145. u32 value = data[priv->sreg].data[0];
  146. #endif
  147. enum ip_conntrack_info ctinfo;
  148. struct nf_conn *ct;
  149. ct = nf_ct_get(skb, &ctinfo);
  150. if (ct == NULL)
  151. return;
  152. switch (priv->key) {
  153. #ifdef CONFIG_NF_CONNTRACK_MARK
  154. case NFT_CT_MARK:
  155. if (ct->mark != value) {
  156. ct->mark = value;
  157. nf_conntrack_event_cache(IPCT_MARK, ct);
  158. }
  159. break;
  160. #endif
  161. }
  162. }
  163. static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
  164. [NFTA_CT_DREG] = { .type = NLA_U32 },
  165. [NFTA_CT_KEY] = { .type = NLA_U32 },
  166. [NFTA_CT_DIRECTION] = { .type = NLA_U8 },
  167. [NFTA_CT_SREG] = { .type = NLA_U32 },
  168. };
  169. static int nft_ct_l3proto_try_module_get(uint8_t family)
  170. {
  171. int err;
  172. if (family == NFPROTO_INET) {
  173. err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
  174. if (err < 0)
  175. goto err1;
  176. err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
  177. if (err < 0)
  178. goto err2;
  179. } else {
  180. err = nf_ct_l3proto_try_module_get(family);
  181. if (err < 0)
  182. goto err1;
  183. }
  184. return 0;
  185. err2:
  186. nf_ct_l3proto_module_put(NFPROTO_IPV4);
  187. err1:
  188. return err;
  189. }
  190. static void nft_ct_l3proto_module_put(uint8_t family)
  191. {
  192. if (family == NFPROTO_INET) {
  193. nf_ct_l3proto_module_put(NFPROTO_IPV4);
  194. nf_ct_l3proto_module_put(NFPROTO_IPV6);
  195. } else
  196. nf_ct_l3proto_module_put(family);
  197. }
  198. static int nft_ct_get_init(const struct nft_ctx *ctx,
  199. const struct nft_expr *expr,
  200. const struct nlattr * const tb[])
  201. {
  202. struct nft_ct *priv = nft_expr_priv(expr);
  203. int err;
  204. priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
  205. switch (priv->key) {
  206. case NFT_CT_STATE:
  207. case NFT_CT_DIRECTION:
  208. case NFT_CT_STATUS:
  209. #ifdef CONFIG_NF_CONNTRACK_MARK
  210. case NFT_CT_MARK:
  211. #endif
  212. #ifdef CONFIG_NF_CONNTRACK_SECMARK
  213. case NFT_CT_SECMARK:
  214. #endif
  215. #ifdef CONFIG_NF_CONNTRACK_LABELS
  216. case NFT_CT_LABELS:
  217. #endif
  218. case NFT_CT_EXPIRATION:
  219. case NFT_CT_HELPER:
  220. if (tb[NFTA_CT_DIRECTION] != NULL)
  221. return -EINVAL;
  222. break;
  223. case NFT_CT_L3PROTOCOL:
  224. case NFT_CT_PROTOCOL:
  225. case NFT_CT_SRC:
  226. case NFT_CT_DST:
  227. case NFT_CT_PROTO_SRC:
  228. case NFT_CT_PROTO_DST:
  229. if (tb[NFTA_CT_DIRECTION] == NULL)
  230. return -EINVAL;
  231. break;
  232. default:
  233. return -EOPNOTSUPP;
  234. }
  235. if (tb[NFTA_CT_DIRECTION] != NULL) {
  236. priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
  237. switch (priv->dir) {
  238. case IP_CT_DIR_ORIGINAL:
  239. case IP_CT_DIR_REPLY:
  240. break;
  241. default:
  242. return -EINVAL;
  243. }
  244. }
  245. priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
  246. err = nft_validate_output_register(priv->dreg);
  247. if (err < 0)
  248. return err;
  249. err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
  250. if (err < 0)
  251. return err;
  252. err = nft_ct_l3proto_try_module_get(ctx->afi->family);
  253. if (err < 0)
  254. return err;
  255. return 0;
  256. }
  257. static int nft_ct_set_init(const struct nft_ctx *ctx,
  258. const struct nft_expr *expr,
  259. const struct nlattr * const tb[])
  260. {
  261. struct nft_ct *priv = nft_expr_priv(expr);
  262. int err;
  263. priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
  264. switch (priv->key) {
  265. #ifdef CONFIG_NF_CONNTRACK_MARK
  266. case NFT_CT_MARK:
  267. break;
  268. #endif
  269. default:
  270. return -EOPNOTSUPP;
  271. }
  272. priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
  273. err = nft_validate_input_register(priv->sreg);
  274. if (err < 0)
  275. return err;
  276. err = nft_ct_l3proto_try_module_get(ctx->afi->family);
  277. if (err < 0)
  278. return err;
  279. return 0;
  280. }
  281. static void nft_ct_destroy(const struct nft_ctx *ctx,
  282. const struct nft_expr *expr)
  283. {
  284. nft_ct_l3proto_module_put(ctx->afi->family);
  285. }
  286. static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
  287. {
  288. const struct nft_ct *priv = nft_expr_priv(expr);
  289. if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg)))
  290. goto nla_put_failure;
  291. if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
  292. goto nla_put_failure;
  293. switch (priv->key) {
  294. case NFT_CT_PROTOCOL:
  295. case NFT_CT_SRC:
  296. case NFT_CT_DST:
  297. case NFT_CT_PROTO_SRC:
  298. case NFT_CT_PROTO_DST:
  299. if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
  300. goto nla_put_failure;
  301. default:
  302. break;
  303. }
  304. return 0;
  305. nla_put_failure:
  306. return -1;
  307. }
  308. static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
  309. {
  310. const struct nft_ct *priv = nft_expr_priv(expr);
  311. if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg)))
  312. goto nla_put_failure;
  313. if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
  314. goto nla_put_failure;
  315. return 0;
  316. nla_put_failure:
  317. return -1;
  318. }
  319. static struct nft_expr_type nft_ct_type;
  320. static const struct nft_expr_ops nft_ct_get_ops = {
  321. .type = &nft_ct_type,
  322. .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
  323. .eval = nft_ct_get_eval,
  324. .init = nft_ct_get_init,
  325. .destroy = nft_ct_destroy,
  326. .dump = nft_ct_get_dump,
  327. };
  328. static const struct nft_expr_ops nft_ct_set_ops = {
  329. .type = &nft_ct_type,
  330. .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
  331. .eval = nft_ct_set_eval,
  332. .init = nft_ct_set_init,
  333. .destroy = nft_ct_destroy,
  334. .dump = nft_ct_set_dump,
  335. };
  336. static const struct nft_expr_ops *
  337. nft_ct_select_ops(const struct nft_ctx *ctx,
  338. const struct nlattr * const tb[])
  339. {
  340. if (tb[NFTA_CT_KEY] == NULL)
  341. return ERR_PTR(-EINVAL);
  342. if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
  343. return ERR_PTR(-EINVAL);
  344. if (tb[NFTA_CT_DREG])
  345. return &nft_ct_get_ops;
  346. if (tb[NFTA_CT_SREG])
  347. return &nft_ct_set_ops;
  348. return ERR_PTR(-EINVAL);
  349. }
  350. static struct nft_expr_type nft_ct_type __read_mostly = {
  351. .name = "ct",
  352. .select_ops = &nft_ct_select_ops,
  353. .policy = nft_ct_policy,
  354. .maxattr = NFTA_CT_MAX,
  355. .owner = THIS_MODULE,
  356. };
  357. static int __init nft_ct_module_init(void)
  358. {
  359. return nft_register_expr(&nft_ct_type);
  360. }
  361. static void __exit nft_ct_module_exit(void)
  362. {
  363. nft_unregister_expr(&nft_ct_type);
  364. }
  365. module_init(nft_ct_module_init);
  366. module_exit(nft_ct_module_exit);
  367. MODULE_LICENSE("GPL");
  368. MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
  369. MODULE_ALIAS_NFT_EXPR("ct");