xt_dccp.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. * iptables module for DCCP protocol header matching
  3. *
  4. * (C) 2005 by Harald Welte <laforge@netfilter.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #include <linux/module.h>
  11. #include <linux/skbuff.h>
  12. #include <linux/slab.h>
  13. #include <linux/spinlock.h>
  14. #include <net/ip.h>
  15. #include <linux/dccp.h>
  16. #include <linux/netfilter/x_tables.h>
  17. #include <linux/netfilter/xt_dccp.h>
  18. #include <linux/netfilter_ipv4/ip_tables.h>
  19. #include <linux/netfilter_ipv6/ip6_tables.h>
  20. MODULE_LICENSE("GPL");
  21. MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  22. MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
  23. MODULE_ALIAS("ipt_dccp");
  24. MODULE_ALIAS("ip6t_dccp");
  25. #define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
  26. || (!!((invflag) & (option)) ^ (cond)))
  27. static unsigned char *dccp_optbuf;
  28. static DEFINE_SPINLOCK(dccp_buflock);
  29. static inline bool
  30. dccp_find_option(u_int8_t option,
  31. const struct sk_buff *skb,
  32. unsigned int protoff,
  33. const struct dccp_hdr *dh,
  34. bool *hotdrop)
  35. {
  36. /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
  37. const unsigned char *op;
  38. unsigned int optoff = __dccp_hdr_len(dh);
  39. unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
  40. unsigned int i;
  41. if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
  42. goto invalid;
  43. if (!optlen)
  44. return false;
  45. spin_lock_bh(&dccp_buflock);
  46. op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
  47. if (op == NULL) {
  48. /* If we don't have the whole header, drop packet. */
  49. goto partial;
  50. }
  51. for (i = 0; i < optlen; ) {
  52. if (op[i] == option) {
  53. spin_unlock_bh(&dccp_buflock);
  54. return true;
  55. }
  56. if (op[i] < 2)
  57. i++;
  58. else
  59. i += op[i+1]?:1;
  60. }
  61. spin_unlock_bh(&dccp_buflock);
  62. return false;
  63. partial:
  64. spin_unlock_bh(&dccp_buflock);
  65. invalid:
  66. *hotdrop = true;
  67. return false;
  68. }
  69. static inline bool
  70. match_types(const struct dccp_hdr *dh, u_int16_t typemask)
  71. {
  72. return typemask & (1 << dh->dccph_type);
  73. }
  74. static inline bool
  75. match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
  76. const struct dccp_hdr *dh, bool *hotdrop)
  77. {
  78. return dccp_find_option(option, skb, protoff, dh, hotdrop);
  79. }
  80. static bool
  81. dccp_mt(const struct sk_buff *skb, struct xt_action_param *par)
  82. {
  83. const struct xt_dccp_info *info = par->matchinfo;
  84. const struct dccp_hdr *dh;
  85. struct dccp_hdr _dh;
  86. if (par->fragoff != 0)
  87. return false;
  88. dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh);
  89. if (dh == NULL) {
  90. par->hotdrop = true;
  91. return false;
  92. }
  93. return DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
  94. && ntohs(dh->dccph_sport) <= info->spts[1],
  95. XT_DCCP_SRC_PORTS, info->flags, info->invflags)
  96. && DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
  97. && ntohs(dh->dccph_dport) <= info->dpts[1],
  98. XT_DCCP_DEST_PORTS, info->flags, info->invflags)
  99. && DCCHECK(match_types(dh, info->typemask),
  100. XT_DCCP_TYPE, info->flags, info->invflags)
  101. && DCCHECK(match_option(info->option, skb, par->thoff, dh,
  102. &par->hotdrop),
  103. XT_DCCP_OPTION, info->flags, info->invflags);
  104. }
  105. static int dccp_mt_check(const struct xt_mtchk_param *par)
  106. {
  107. const struct xt_dccp_info *info = par->matchinfo;
  108. if (info->flags & ~XT_DCCP_VALID_FLAGS)
  109. return -EINVAL;
  110. if (info->invflags & ~XT_DCCP_VALID_FLAGS)
  111. return -EINVAL;
  112. if (info->invflags & ~info->flags)
  113. return -EINVAL;
  114. return 0;
  115. }
  116. static struct xt_match dccp_mt_reg[] __read_mostly = {
  117. {
  118. .name = "dccp",
  119. .family = NFPROTO_IPV4,
  120. .checkentry = dccp_mt_check,
  121. .match = dccp_mt,
  122. .matchsize = sizeof(struct xt_dccp_info),
  123. .proto = IPPROTO_DCCP,
  124. .me = THIS_MODULE,
  125. },
  126. {
  127. .name = "dccp",
  128. .family = NFPROTO_IPV6,
  129. .checkentry = dccp_mt_check,
  130. .match = dccp_mt,
  131. .matchsize = sizeof(struct xt_dccp_info),
  132. .proto = IPPROTO_DCCP,
  133. .me = THIS_MODULE,
  134. },
  135. };
  136. static int __init dccp_mt_init(void)
  137. {
  138. int ret;
  139. /* doff is 8 bits, so the maximum option size is (4*256). Don't put
  140. * this in BSS since DaveM is worried about locked TLB's for kernel
  141. * BSS. */
  142. dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
  143. if (!dccp_optbuf)
  144. return -ENOMEM;
  145. ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
  146. if (ret)
  147. goto out_kfree;
  148. return ret;
  149. out_kfree:
  150. kfree(dccp_optbuf);
  151. return ret;
  152. }
  153. static void __exit dccp_mt_exit(void)
  154. {
  155. xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
  156. kfree(dccp_optbuf);
  157. }
  158. module_init(dccp_mt_init);
  159. module_exit(dccp_mt_exit);