ip_set_getport.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@netfilter.org>
  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. /* Get Layer-4 data from the packets */
  9. #include <linux/ip.h>
  10. #include <linux/skbuff.h>
  11. #include <linux/icmp.h>
  12. #include <linux/icmpv6.h>
  13. #include <linux/sctp.h>
  14. #include <linux/netfilter_ipv6/ip6_tables.h>
  15. #include <net/ip.h>
  16. #include <net/ipv6.h>
  17. #include <linux/netfilter/ipset/ip_set_getport.h>
  18. #include <linux/export.h>
  19. /* We must handle non-linear skbs */
  20. static bool
  21. get_port(const struct sk_buff *skb, int protocol, unsigned int protooff,
  22. bool src, __be16 *port, u8 *proto)
  23. {
  24. switch (protocol) {
  25. case IPPROTO_TCP: {
  26. struct tcphdr _tcph;
  27. const struct tcphdr *th;
  28. th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph);
  29. if (!th)
  30. /* No choice either */
  31. return false;
  32. *port = src ? th->source : th->dest;
  33. break;
  34. }
  35. case IPPROTO_SCTP: {
  36. struct sctphdr _sh;
  37. const struct sctphdr *sh;
  38. sh = skb_header_pointer(skb, protooff, sizeof(_sh), &_sh);
  39. if (!sh)
  40. /* No choice either */
  41. return false;
  42. *port = src ? sh->source : sh->dest;
  43. break;
  44. }
  45. case IPPROTO_UDP:
  46. case IPPROTO_UDPLITE: {
  47. struct udphdr _udph;
  48. const struct udphdr *uh;
  49. uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph);
  50. if (!uh)
  51. /* No choice either */
  52. return false;
  53. *port = src ? uh->source : uh->dest;
  54. break;
  55. }
  56. case IPPROTO_ICMP: {
  57. struct icmphdr _ich;
  58. const struct icmphdr *ic;
  59. ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich);
  60. if (!ic)
  61. return false;
  62. *port = (__force __be16)htons((ic->type << 8) | ic->code);
  63. break;
  64. }
  65. case IPPROTO_ICMPV6: {
  66. struct icmp6hdr _ich;
  67. const struct icmp6hdr *ic;
  68. ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich);
  69. if (!ic)
  70. return false;
  71. *port = (__force __be16)
  72. htons((ic->icmp6_type << 8) | ic->icmp6_code);
  73. break;
  74. }
  75. default:
  76. break;
  77. }
  78. *proto = protocol;
  79. return true;
  80. }
  81. bool
  82. ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
  83. __be16 *port, u8 *proto)
  84. {
  85. const struct iphdr *iph = ip_hdr(skb);
  86. unsigned int protooff = skb_network_offset(skb) + ip_hdrlen(skb);
  87. int protocol = iph->protocol;
  88. /* See comments at tcp_match in ip_tables.c */
  89. if (protocol <= 0)
  90. return false;
  91. if (ntohs(iph->frag_off) & IP_OFFSET)
  92. switch (protocol) {
  93. case IPPROTO_TCP:
  94. case IPPROTO_SCTP:
  95. case IPPROTO_UDP:
  96. case IPPROTO_UDPLITE:
  97. case IPPROTO_ICMP:
  98. /* Port info not available for fragment offset > 0 */
  99. return false;
  100. default:
  101. /* Other protocols doesn't have ports,
  102. * so we can match fragments.
  103. */
  104. *proto = protocol;
  105. return true;
  106. }
  107. return get_port(skb, protocol, protooff, src, port, proto);
  108. }
  109. EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
  110. #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
  111. bool
  112. ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
  113. __be16 *port, u8 *proto)
  114. {
  115. int protoff;
  116. u8 nexthdr;
  117. __be16 frag_off = 0;
  118. nexthdr = ipv6_hdr(skb)->nexthdr;
  119. protoff = ipv6_skip_exthdr(skb,
  120. skb_network_offset(skb) +
  121. sizeof(struct ipv6hdr), &nexthdr,
  122. &frag_off);
  123. if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
  124. return false;
  125. return get_port(skb, nexthdr, protoff, src, port, proto);
  126. }
  127. EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
  128. #endif