em_ipt.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * net/sched/em_ipt.c IPtables matches Ematch
  3. *
  4. * (c) 2018 Eyal Birger <eyal.birger@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version
  9. * 2 of the License, or (at your option) any later version.
  10. */
  11. #include <linux/gfp.h>
  12. #include <linux/module.h>
  13. #include <linux/types.h>
  14. #include <linux/kernel.h>
  15. #include <linux/string.h>
  16. #include <linux/skbuff.h>
  17. #include <linux/tc_ematch/tc_em_ipt.h>
  18. #include <linux/netfilter.h>
  19. #include <linux/netfilter/x_tables.h>
  20. #include <linux/netfilter_ipv4/ip_tables.h>
  21. #include <linux/netfilter_ipv6/ip6_tables.h>
  22. #include <net/pkt_cls.h>
  23. struct em_ipt_match {
  24. const struct xt_match *match;
  25. u32 hook;
  26. u8 match_data[0] __aligned(8);
  27. };
  28. struct em_ipt_xt_match {
  29. char *match_name;
  30. int (*validate_match_data)(struct nlattr **tb, u8 mrev);
  31. };
  32. static const struct nla_policy em_ipt_policy[TCA_EM_IPT_MAX + 1] = {
  33. [TCA_EM_IPT_MATCH_NAME] = { .type = NLA_STRING,
  34. .len = XT_EXTENSION_MAXNAMELEN },
  35. [TCA_EM_IPT_MATCH_REVISION] = { .type = NLA_U8 },
  36. [TCA_EM_IPT_HOOK] = { .type = NLA_U32 },
  37. [TCA_EM_IPT_NFPROTO] = { .type = NLA_U8 },
  38. [TCA_EM_IPT_MATCH_DATA] = { .type = NLA_UNSPEC },
  39. };
  40. static int check_match(struct net *net, struct em_ipt_match *im, int mdata_len)
  41. {
  42. struct xt_mtchk_param mtpar = {};
  43. union {
  44. struct ipt_entry e4;
  45. struct ip6t_entry e6;
  46. } e = {};
  47. mtpar.net = net;
  48. mtpar.table = "filter";
  49. mtpar.hook_mask = 1 << im->hook;
  50. mtpar.family = im->match->family;
  51. mtpar.match = im->match;
  52. mtpar.entryinfo = &e;
  53. mtpar.matchinfo = (void *)im->match_data;
  54. return xt_check_match(&mtpar, mdata_len, 0, 0);
  55. }
  56. static int policy_validate_match_data(struct nlattr **tb, u8 mrev)
  57. {
  58. if (mrev != 0) {
  59. pr_err("only policy match revision 0 supported");
  60. return -EINVAL;
  61. }
  62. if (nla_get_u32(tb[TCA_EM_IPT_HOOK]) != NF_INET_PRE_ROUTING) {
  63. pr_err("policy can only be matched on NF_INET_PRE_ROUTING");
  64. return -EINVAL;
  65. }
  66. return 0;
  67. }
  68. static const struct em_ipt_xt_match em_ipt_xt_matches[] = {
  69. {
  70. .match_name = "policy",
  71. .validate_match_data = policy_validate_match_data
  72. },
  73. {}
  74. };
  75. static struct xt_match *get_xt_match(struct nlattr **tb)
  76. {
  77. const struct em_ipt_xt_match *m;
  78. struct nlattr *mname_attr;
  79. u8 nfproto, mrev = 0;
  80. int ret;
  81. mname_attr = tb[TCA_EM_IPT_MATCH_NAME];
  82. for (m = em_ipt_xt_matches; m->match_name; m++) {
  83. if (!nla_strcmp(mname_attr, m->match_name))
  84. break;
  85. }
  86. if (!m->match_name) {
  87. pr_err("Unsupported xt match");
  88. return ERR_PTR(-EINVAL);
  89. }
  90. if (tb[TCA_EM_IPT_MATCH_REVISION])
  91. mrev = nla_get_u8(tb[TCA_EM_IPT_MATCH_REVISION]);
  92. ret = m->validate_match_data(tb, mrev);
  93. if (ret < 0)
  94. return ERR_PTR(ret);
  95. nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]);
  96. return xt_request_find_match(nfproto, m->match_name, mrev);
  97. }
  98. static int em_ipt_change(struct net *net, void *data, int data_len,
  99. struct tcf_ematch *em)
  100. {
  101. struct nlattr *tb[TCA_EM_IPT_MAX + 1];
  102. struct em_ipt_match *im = NULL;
  103. struct xt_match *match;
  104. int mdata_len, ret;
  105. ret = nla_parse(tb, TCA_EM_IPT_MAX, data, data_len, em_ipt_policy,
  106. NULL);
  107. if (ret < 0)
  108. return ret;
  109. if (!tb[TCA_EM_IPT_HOOK] || !tb[TCA_EM_IPT_MATCH_NAME] ||
  110. !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO])
  111. return -EINVAL;
  112. match = get_xt_match(tb);
  113. if (IS_ERR(match)) {
  114. pr_err("unable to load match\n");
  115. return PTR_ERR(match);
  116. }
  117. mdata_len = XT_ALIGN(nla_len(tb[TCA_EM_IPT_MATCH_DATA]));
  118. im = kzalloc(sizeof(*im) + mdata_len, GFP_KERNEL);
  119. if (!im) {
  120. ret = -ENOMEM;
  121. goto err;
  122. }
  123. im->match = match;
  124. im->hook = nla_get_u32(tb[TCA_EM_IPT_HOOK]);
  125. nla_memcpy(im->match_data, tb[TCA_EM_IPT_MATCH_DATA], mdata_len);
  126. ret = check_match(net, im, mdata_len);
  127. if (ret)
  128. goto err;
  129. em->datalen = sizeof(*im) + mdata_len;
  130. em->data = (unsigned long)im;
  131. return 0;
  132. err:
  133. kfree(im);
  134. module_put(match->me);
  135. return ret;
  136. }
  137. static void em_ipt_destroy(struct tcf_ematch *em)
  138. {
  139. struct em_ipt_match *im = (void *)em->data;
  140. if (!im)
  141. return;
  142. if (im->match->destroy) {
  143. struct xt_mtdtor_param par = {
  144. .net = em->net,
  145. .match = im->match,
  146. .matchinfo = im->match_data,
  147. .family = im->match->family
  148. };
  149. im->match->destroy(&par);
  150. }
  151. module_put(im->match->me);
  152. kfree((void *)im);
  153. }
  154. static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em,
  155. struct tcf_pkt_info *info)
  156. {
  157. const struct em_ipt_match *im = (const void *)em->data;
  158. struct xt_action_param acpar = {};
  159. struct net_device *indev = NULL;
  160. struct nf_hook_state state;
  161. int ret;
  162. rcu_read_lock();
  163. if (skb->skb_iif)
  164. indev = dev_get_by_index_rcu(em->net, skb->skb_iif);
  165. nf_hook_state_init(&state, im->hook, im->match->family,
  166. indev ?: skb->dev, skb->dev, NULL, em->net, NULL);
  167. acpar.match = im->match;
  168. acpar.matchinfo = im->match_data;
  169. acpar.state = &state;
  170. ret = im->match->match(skb, &acpar);
  171. rcu_read_unlock();
  172. return ret;
  173. }
  174. static int em_ipt_dump(struct sk_buff *skb, struct tcf_ematch *em)
  175. {
  176. struct em_ipt_match *im = (void *)em->data;
  177. if (nla_put_string(skb, TCA_EM_IPT_MATCH_NAME, im->match->name) < 0)
  178. return -EMSGSIZE;
  179. if (nla_put_u32(skb, TCA_EM_IPT_HOOK, im->hook) < 0)
  180. return -EMSGSIZE;
  181. if (nla_put_u8(skb, TCA_EM_IPT_MATCH_REVISION, im->match->revision) < 0)
  182. return -EMSGSIZE;
  183. if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->match->family) < 0)
  184. return -EMSGSIZE;
  185. if (nla_put(skb, TCA_EM_IPT_MATCH_DATA,
  186. im->match->usersize ?: im->match->matchsize,
  187. im->match_data) < 0)
  188. return -EMSGSIZE;
  189. return 0;
  190. }
  191. static struct tcf_ematch_ops em_ipt_ops = {
  192. .kind = TCF_EM_IPT,
  193. .change = em_ipt_change,
  194. .destroy = em_ipt_destroy,
  195. .match = em_ipt_match,
  196. .dump = em_ipt_dump,
  197. .owner = THIS_MODULE,
  198. .link = LIST_HEAD_INIT(em_ipt_ops.link)
  199. };
  200. static int __init init_em_ipt(void)
  201. {
  202. return tcf_em_register(&em_ipt_ops);
  203. }
  204. static void __exit exit_em_ipt(void)
  205. {
  206. tcf_em_unregister(&em_ipt_ops);
  207. }
  208. MODULE_LICENSE("GPL");
  209. MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>");
  210. MODULE_DESCRIPTION("TC extended match for IPtables matches");
  211. module_init(init_em_ipt);
  212. module_exit(exit_em_ipt);
  213. MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT);