nf_sockopt.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #include <linux/kernel.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/skbuff.h>
  5. #include <linux/netfilter.h>
  6. #include <linux/mutex.h>
  7. #include <net/sock.h>
  8. #include "nf_internals.h"
  9. /* Sockopts only registered and called from user context, so
  10. net locking would be overkill. Also, [gs]etsockopt calls may
  11. sleep. */
  12. static DEFINE_MUTEX(nf_sockopt_mutex);
  13. static LIST_HEAD(nf_sockopts);
  14. /* Do exclusive ranges overlap? */
  15. static inline int overlap(int min1, int max1, int min2, int max2)
  16. {
  17. return max1 > min2 && min1 < max2;
  18. }
  19. /* Functions to register sockopt ranges (exclusive). */
  20. int nf_register_sockopt(struct nf_sockopt_ops *reg)
  21. {
  22. struct nf_sockopt_ops *ops;
  23. int ret = 0;
  24. if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
  25. return -EINTR;
  26. list_for_each_entry(ops, &nf_sockopts, list) {
  27. if (ops->pf == reg->pf
  28. && (overlap(ops->set_optmin, ops->set_optmax,
  29. reg->set_optmin, reg->set_optmax)
  30. || overlap(ops->get_optmin, ops->get_optmax,
  31. reg->get_optmin, reg->get_optmax))) {
  32. NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
  33. ops->set_optmin, ops->set_optmax,
  34. ops->get_optmin, ops->get_optmax,
  35. reg->set_optmin, reg->set_optmax,
  36. reg->get_optmin, reg->get_optmax);
  37. ret = -EBUSY;
  38. goto out;
  39. }
  40. }
  41. list_add(&reg->list, &nf_sockopts);
  42. out:
  43. mutex_unlock(&nf_sockopt_mutex);
  44. return ret;
  45. }
  46. EXPORT_SYMBOL(nf_register_sockopt);
  47. void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
  48. {
  49. mutex_lock(&nf_sockopt_mutex);
  50. list_del(&reg->list);
  51. mutex_unlock(&nf_sockopt_mutex);
  52. }
  53. EXPORT_SYMBOL(nf_unregister_sockopt);
  54. static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf,
  55. int val, int get)
  56. {
  57. struct nf_sockopt_ops *ops;
  58. if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
  59. return ERR_PTR(-EINTR);
  60. list_for_each_entry(ops, &nf_sockopts, list) {
  61. if (ops->pf == pf) {
  62. if (!try_module_get(ops->owner))
  63. goto out_nosup;
  64. if (get) {
  65. if (val >= ops->get_optmin &&
  66. val < ops->get_optmax)
  67. goto out;
  68. } else {
  69. if (val >= ops->set_optmin &&
  70. val < ops->set_optmax)
  71. goto out;
  72. }
  73. module_put(ops->owner);
  74. }
  75. }
  76. out_nosup:
  77. ops = ERR_PTR(-ENOPROTOOPT);
  78. out:
  79. mutex_unlock(&nf_sockopt_mutex);
  80. return ops;
  81. }
  82. /* Call get/setsockopt() */
  83. static int nf_sockopt(struct sock *sk, u_int8_t pf, int val,
  84. char __user *opt, int *len, int get)
  85. {
  86. struct nf_sockopt_ops *ops;
  87. int ret;
  88. ops = nf_sockopt_find(sk, pf, val, get);
  89. if (IS_ERR(ops))
  90. return PTR_ERR(ops);
  91. if (get)
  92. ret = ops->get(sk, val, opt, len);
  93. else
  94. ret = ops->set(sk, val, opt, *len);
  95. module_put(ops->owner);
  96. return ret;
  97. }
  98. int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
  99. unsigned int len)
  100. {
  101. return nf_sockopt(sk, pf, val, opt, &len, 0);
  102. }
  103. EXPORT_SYMBOL(nf_setsockopt);
  104. int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
  105. int *len)
  106. {
  107. return nf_sockopt(sk, pf, val, opt, len, 1);
  108. }
  109. EXPORT_SYMBOL(nf_getsockopt);
  110. #ifdef CONFIG_COMPAT
  111. static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val,
  112. char __user *opt, int *len, int get)
  113. {
  114. struct nf_sockopt_ops *ops;
  115. int ret;
  116. ops = nf_sockopt_find(sk, pf, val, get);
  117. if (IS_ERR(ops))
  118. return PTR_ERR(ops);
  119. if (get) {
  120. if (ops->compat_get)
  121. ret = ops->compat_get(sk, val, opt, len);
  122. else
  123. ret = ops->get(sk, val, opt, len);
  124. } else {
  125. if (ops->compat_set)
  126. ret = ops->compat_set(sk, val, opt, *len);
  127. else
  128. ret = ops->set(sk, val, opt, *len);
  129. }
  130. module_put(ops->owner);
  131. return ret;
  132. }
  133. int compat_nf_setsockopt(struct sock *sk, u_int8_t pf,
  134. int val, char __user *opt, unsigned int len)
  135. {
  136. return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
  137. }
  138. EXPORT_SYMBOL(compat_nf_setsockopt);
  139. int compat_nf_getsockopt(struct sock *sk, u_int8_t pf,
  140. int val, char __user *opt, int *len)
  141. {
  142. return compat_nf_sockopt(sk, pf, val, opt, len, 1);
  143. }
  144. EXPORT_SYMBOL(compat_nf_getsockopt);
  145. #endif