xt_tcpudp.c (5781B)
1// SPDX-License-Identifier: GPL-2.0-only 2#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3#include <linux/types.h> 4#include <linux/module.h> 5#include <net/ip.h> 6#include <linux/ipv6.h> 7#include <net/ipv6.h> 8#include <net/tcp.h> 9#include <net/udp.h> 10#include <linux/netfilter/x_tables.h> 11#include <linux/netfilter/xt_tcpudp.h> 12#include <linux/netfilter_ipv4/ip_tables.h> 13#include <linux/netfilter_ipv6/ip6_tables.h> 14 15MODULE_DESCRIPTION("Xtables: TCP, UDP and UDP-Lite match"); 16MODULE_LICENSE("GPL"); 17MODULE_ALIAS("xt_tcp"); 18MODULE_ALIAS("xt_udp"); 19MODULE_ALIAS("ipt_udp"); 20MODULE_ALIAS("ipt_tcp"); 21MODULE_ALIAS("ip6t_udp"); 22MODULE_ALIAS("ip6t_tcp"); 23 24/* Returns 1 if the port is matched by the range, 0 otherwise */ 25static inline bool 26port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert) 27{ 28 return (port >= min && port <= max) ^ invert; 29} 30 31static bool 32tcp_find_option(u_int8_t option, 33 const struct sk_buff *skb, 34 unsigned int protoff, 35 unsigned int optlen, 36 bool invert, 37 bool *hotdrop) 38{ 39 /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ 40 const u_int8_t *op; 41 u_int8_t _opt[60 - sizeof(struct tcphdr)]; 42 unsigned int i; 43 44 pr_debug("finding option\n"); 45 46 if (!optlen) 47 return invert; 48 49 /* If we don't have the whole header, drop packet. */ 50 op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr), 51 optlen, _opt); 52 if (op == NULL) { 53 *hotdrop = true; 54 return false; 55 } 56 57 for (i = 0; i < optlen; ) { 58 if (op[i] == option) return !invert; 59 if (op[i] < 2) i++; 60 else i += op[i+1]?:1; 61 } 62 63 return invert; 64} 65 66static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) 67{ 68 const struct tcphdr *th; 69 struct tcphdr _tcph; 70 const struct xt_tcp *tcpinfo = par->matchinfo; 71 72 if (par->fragoff != 0) { 73 /* To quote Alan: 74 75 Don't allow a fragment of TCP 8 bytes in. Nobody normal 76 causes this. Its a cracker trying to break in by doing a 77 flag overwrite to pass the direction checks. 78 */ 79 if (par->fragoff == 1) { 80 pr_debug("Dropping evil TCP offset=1 frag.\n"); 81 par->hotdrop = true; 82 } 83 /* Must not be a fragment. */ 84 return false; 85 } 86 87 th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); 88 if (th == NULL) { 89 /* We've been asked to examine this packet, and we 90 can't. Hence, no choice but to drop. */ 91 pr_debug("Dropping evil TCP offset=0 tinygram.\n"); 92 par->hotdrop = true; 93 return false; 94 } 95 96 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1], 97 ntohs(th->source), 98 !!(tcpinfo->invflags & XT_TCP_INV_SRCPT))) 99 return false; 100 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], 101 ntohs(th->dest), 102 !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) 103 return false; 104 if (!NF_INVF(tcpinfo, XT_TCP_INV_FLAGS, 105 (((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp)) 106 return false; 107 if (tcpinfo->option) { 108 if (th->doff * 4 < sizeof(_tcph)) { 109 par->hotdrop = true; 110 return false; 111 } 112 if (!tcp_find_option(tcpinfo->option, skb, par->thoff, 113 th->doff*4 - sizeof(_tcph), 114 tcpinfo->invflags & XT_TCP_INV_OPTION, 115 &par->hotdrop)) 116 return false; 117 } 118 return true; 119} 120 121static int tcp_mt_check(const struct xt_mtchk_param *par) 122{ 123 const struct xt_tcp *tcpinfo = par->matchinfo; 124 125 /* Must specify no unknown invflags */ 126 return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0; 127} 128 129static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par) 130{ 131 const struct udphdr *uh; 132 struct udphdr _udph; 133 const struct xt_udp *udpinfo = par->matchinfo; 134 135 /* Must not be a fragment. */ 136 if (par->fragoff != 0) 137 return false; 138 139 uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph); 140 if (uh == NULL) { 141 /* We've been asked to examine this packet, and we 142 can't. Hence, no choice but to drop. */ 143 pr_debug("Dropping evil UDP tinygram.\n"); 144 par->hotdrop = true; 145 return false; 146 } 147 148 return port_match(udpinfo->spts[0], udpinfo->spts[1], 149 ntohs(uh->source), 150 !!(udpinfo->invflags & XT_UDP_INV_SRCPT)) 151 && port_match(udpinfo->dpts[0], udpinfo->dpts[1], 152 ntohs(uh->dest), 153 !!(udpinfo->invflags & XT_UDP_INV_DSTPT)); 154} 155 156static int udp_mt_check(const struct xt_mtchk_param *par) 157{ 158 const struct xt_udp *udpinfo = par->matchinfo; 159 160 /* Must specify no unknown invflags */ 161 return (udpinfo->invflags & ~XT_UDP_INV_MASK) ? -EINVAL : 0; 162} 163 164static struct xt_match tcpudp_mt_reg[] __read_mostly = { 165 { 166 .name = "tcp", 167 .family = NFPROTO_IPV4, 168 .checkentry = tcp_mt_check, 169 .match = tcp_mt, 170 .matchsize = sizeof(struct xt_tcp), 171 .proto = IPPROTO_TCP, 172 .me = THIS_MODULE, 173 }, 174 { 175 .name = "tcp", 176 .family = NFPROTO_IPV6, 177 .checkentry = tcp_mt_check, 178 .match = tcp_mt, 179 .matchsize = sizeof(struct xt_tcp), 180 .proto = IPPROTO_TCP, 181 .me = THIS_MODULE, 182 }, 183 { 184 .name = "udp", 185 .family = NFPROTO_IPV4, 186 .checkentry = udp_mt_check, 187 .match = udp_mt, 188 .matchsize = sizeof(struct xt_udp), 189 .proto = IPPROTO_UDP, 190 .me = THIS_MODULE, 191 }, 192 { 193 .name = "udp", 194 .family = NFPROTO_IPV6, 195 .checkentry = udp_mt_check, 196 .match = udp_mt, 197 .matchsize = sizeof(struct xt_udp), 198 .proto = IPPROTO_UDP, 199 .me = THIS_MODULE, 200 }, 201 { 202 .name = "udplite", 203 .family = NFPROTO_IPV4, 204 .checkentry = udp_mt_check, 205 .match = udp_mt, 206 .matchsize = sizeof(struct xt_udp), 207 .proto = IPPROTO_UDPLITE, 208 .me = THIS_MODULE, 209 }, 210 { 211 .name = "udplite", 212 .family = NFPROTO_IPV6, 213 .checkentry = udp_mt_check, 214 .match = udp_mt, 215 .matchsize = sizeof(struct xt_udp), 216 .proto = IPPROTO_UDPLITE, 217 .me = THIS_MODULE, 218 }, 219}; 220 221static int __init tcpudp_mt_init(void) 222{ 223 return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); 224} 225 226static void __exit tcpudp_mt_exit(void) 227{ 228 xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); 229} 230 231module_init(tcpudp_mt_init); 232module_exit(tcpudp_mt_exit);