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 <linux/icmp.h> 8 #include <net/ipv6.h> 9 #include <net/tcp.h> 10 #include <net/udp.h> 11 #include <linux/netfilter/x_tables.h> 12 #include <linux/netfilter/xt_tcpudp.h> 13 #include <linux/netfilter_ipv4/ip_tables.h> 14 #include <linux/netfilter_ipv6/ip6_tables.h> 15 16 MODULE_DESCRIPTION("Xtables: TCP, UDP and UDP-Lite match"); 17 MODULE_LICENSE("GPL"); 18 MODULE_ALIAS("xt_tcp"); 19 MODULE_ALIAS("xt_udp"); 20 MODULE_ALIAS("ipt_udp"); 21 MODULE_ALIAS("ipt_tcp"); 22 MODULE_ALIAS("ip6t_udp"); 23 MODULE_ALIAS("ip6t_tcp"); 24 MODULE_ALIAS("ipt_icmp"); 25 MODULE_ALIAS("ip6t_icmp6"); 26 27 /* Returns 1 if the port is matched by the range, 0 otherwise */ 28 static inline bool 29 port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert) 30 { 31 return (port >= min && port <= max) ^ invert; 32 } 33 34 static bool 35 tcp_find_option(u_int8_t option, 36 const struct sk_buff *skb, 37 unsigned int protoff, 38 unsigned int optlen, 39 bool invert, 40 bool *hotdrop) 41 { 42 /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ 43 const u_int8_t *op; 44 u_int8_t _opt[60 - sizeof(struct tcphdr)]; 45 unsigned int i; 46 47 pr_debug("finding option\n"); 48 49 if (!optlen) 50 return invert; 51 52 /* If we don't have the whole header, drop packet. */ 53 op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr), 54 optlen, _opt); 55 if (op == NULL) { 56 *hotdrop = true; 57 return false; 58 } 59 60 for (i = 0; i < optlen; ) { 61 if (op[i] == option) return !invert; 62 if (op[i] < 2) i++; 63 else i += op[i+1]?:1; 64 } 65 66 return invert; 67 } 68 69 static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) 70 { 71 const struct tcphdr *th; 72 struct tcphdr _tcph; 73 const struct xt_tcp *tcpinfo = par->matchinfo; 74 75 if (par->fragoff != 0) { 76 /* To quote Alan: 77 78 Don't allow a fragment of TCP 8 bytes in. Nobody normal 79 causes this. Its a cracker trying to break in by doing a 80 flag overwrite to pass the direction checks. 81 */ 82 if (par->fragoff == 1) { 83 pr_debug("Dropping evil TCP offset=1 frag.\n"); 84 par->hotdrop = true; 85 } 86 /* Must not be a fragment. */ 87 return false; 88 } 89 90 th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); 91 if (th == NULL) { 92 /* We've been asked to examine this packet, and we 93 can't. Hence, no choice but to drop. */ 94 pr_debug("Dropping evil TCP offset=0 tinygram.\n"); 95 par->hotdrop = true; 96 return false; 97 } 98 99 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1], 100 ntohs(th->source), 101 !!(tcpinfo->invflags & XT_TCP_INV_SRCPT))) 102 return false; 103 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], 104 ntohs(th->dest), 105 !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) 106 return false; 107 if (!NF_INVF(tcpinfo, XT_TCP_INV_FLAGS, 108 (((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp)) 109 return false; 110 if (tcpinfo->option) { 111 if (th->doff * 4 < sizeof(_tcph)) { 112 par->hotdrop = true; 113 return false; 114 } 115 if (!tcp_find_option(tcpinfo->option, skb, par->thoff, 116 th->doff*4 - sizeof(_tcph), 117 tcpinfo->invflags & XT_TCP_INV_OPTION, 118 &par->hotdrop)) 119 return false; 120 } 121 return true; 122 } 123 124 static int tcp_mt_check(const struct xt_mtchk_param *par) 125 { 126 const struct xt_tcp *tcpinfo = par->matchinfo; 127 128 /* Must specify no unknown invflags */ 129 return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0; 130 } 131 132 static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par) 133 { 134 const struct udphdr *uh; 135 struct udphdr _udph; 136 const struct xt_udp *udpinfo = par->matchinfo; 137 138 /* Must not be a fragment. */ 139 if (par->fragoff != 0) 140 return false; 141 142 uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph); 143 if (uh == NULL) { 144 /* We've been asked to examine this packet, and we 145 can't. Hence, no choice but to drop. */ 146 pr_debug("Dropping evil UDP tinygram.\n"); 147 par->hotdrop = true; 148 return false; 149 } 150 151 return port_match(udpinfo->spts[0], udpinfo->spts[1], 152 ntohs(uh->source), 153 !!(udpinfo->invflags & XT_UDP_INV_SRCPT)) 154 && port_match(udpinfo->dpts[0], udpinfo->dpts[1], 155 ntohs(uh->dest), 156 !!(udpinfo->invflags & XT_UDP_INV_DSTPT)); 157 } 158 159 static int udp_mt_check(const struct xt_mtchk_param *par) 160 { 161 const struct xt_udp *udpinfo = par->matchinfo; 162 163 /* Must specify no unknown invflags */ 164 return (udpinfo->invflags & ~XT_UDP_INV_MASK) ? -EINVAL : 0; 165 } 166 167 /* Returns 1 if the type and code is matched by the range, 0 otherwise */ 168 static bool type_code_in_range(u8 test_type, u8 min_code, u8 max_code, 169 u8 type, u8 code) 170 { 171 return type == test_type && code >= min_code && code <= max_code; 172 } 173 174 static bool icmp_type_code_match(u8 test_type, u8 min_code, u8 max_code, 175 u8 type, u8 code, bool invert) 176 { 177 return (test_type == 0xFF || 178 type_code_in_range(test_type, min_code, max_code, type, code)) 179 ^ invert; 180 } 181 182 static bool icmp6_type_code_match(u8 test_type, u8 min_code, u8 max_code, 183 u8 type, u8 code, bool invert) 184 { 185 return type_code_in_range(test_type, min_code, max_code, type, code) ^ invert; 186 } 187 188 static bool 189 icmp_match(const struct sk_buff *skb, struct xt_action_param *par) 190 { 191 const struct icmphdr *ic; 192 struct icmphdr _icmph; 193 const struct ipt_icmp *icmpinfo = par->matchinfo; 194 195 /* Must not be a fragment. */ 196 if (par->fragoff != 0) 197 return false; 198 199 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph); 200 if (!ic) { 201 /* We've been asked to examine this packet, and we 202 * can't. Hence, no choice but to drop. 203 */ 204 par->hotdrop = true; 205 return false; 206 } 207 208 return icmp_type_code_match(icmpinfo->type, 209 icmpinfo->code[0], 210 icmpinfo->code[1], 211 ic->type, ic->code, 212 !!(icmpinfo->invflags & IPT_ICMP_INV)); 213 } 214 215 static bool 216 icmp6_match(const struct sk_buff *skb, struct xt_action_param *par) 217 { 218 const struct icmp6hdr *ic; 219 struct icmp6hdr _icmph; 220 const struct ip6t_icmp *icmpinfo = par->matchinfo; 221 222 /* Must not be a fragment. */ 223 if (par->fragoff != 0) 224 return false; 225 226 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph); 227 if (!ic) { 228 /* We've been asked to examine this packet, and we 229 * can't. Hence, no choice but to drop. 230 */ 231 par->hotdrop = true; 232 return false; 233 } 234 235 return icmp6_type_code_match(icmpinfo->type, 236 icmpinfo->code[0], 237 icmpinfo->code[1], 238 ic->icmp6_type, ic->icmp6_code, 239 !!(icmpinfo->invflags & IP6T_ICMP_INV)); 240 } 241 242 static int icmp_checkentry(const struct xt_mtchk_param *par) 243 { 244 const struct ipt_icmp *icmpinfo = par->matchinfo; 245 246 return (icmpinfo->invflags & ~IPT_ICMP_INV) ? -EINVAL : 0; 247 } 248 249 static int icmp6_checkentry(const struct xt_mtchk_param *par) 250 { 251 const struct ip6t_icmp *icmpinfo = par->matchinfo; 252 253 return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0; 254 } 255 256 static struct xt_match tcpudp_mt_reg[] __read_mostly = { 257 { 258 .name = "tcp", 259 .family = NFPROTO_IPV4, 260 .checkentry = tcp_mt_check, 261 .match = tcp_mt, 262 .matchsize = sizeof(struct xt_tcp), 263 .proto = IPPROTO_TCP, 264 .me = THIS_MODULE, 265 }, 266 { 267 .name = "tcp", 268 .family = NFPROTO_IPV6, 269 .checkentry = tcp_mt_check, 270 .match = tcp_mt, 271 .matchsize = sizeof(struct xt_tcp), 272 .proto = IPPROTO_TCP, 273 .me = THIS_MODULE, 274 }, 275 { 276 .name = "udp", 277 .family = NFPROTO_IPV4, 278 .checkentry = udp_mt_check, 279 .match = udp_mt, 280 .matchsize = sizeof(struct xt_udp), 281 .proto = IPPROTO_UDP, 282 .me = THIS_MODULE, 283 }, 284 { 285 .name = "udp", 286 .family = NFPROTO_IPV6, 287 .checkentry = udp_mt_check, 288 .match = udp_mt, 289 .matchsize = sizeof(struct xt_udp), 290 .proto = IPPROTO_UDP, 291 .me = THIS_MODULE, 292 }, 293 { 294 .name = "udplite", 295 .family = NFPROTO_IPV4, 296 .checkentry = udp_mt_check, 297 .match = udp_mt, 298 .matchsize = sizeof(struct xt_udp), 299 .proto = IPPROTO_UDPLITE, 300 .me = THIS_MODULE, 301 }, 302 { 303 .name = "udplite", 304 .family = NFPROTO_IPV6, 305 .checkentry = udp_mt_check, 306 .match = udp_mt, 307 .matchsize = sizeof(struct xt_udp), 308 .proto = IPPROTO_UDPLITE, 309 .me = THIS_MODULE, 310 }, 311 { 312 .name = "icmp", 313 .match = icmp_match, 314 .matchsize = sizeof(struct ipt_icmp), 315 .checkentry = icmp_checkentry, 316 .proto = IPPROTO_ICMP, 317 .family = NFPROTO_IPV4, 318 .me = THIS_MODULE, 319 }, 320 { 321 .name = "icmp6", 322 .match = icmp6_match, 323 .matchsize = sizeof(struct ip6t_icmp), 324 .checkentry = icmp6_checkentry, 325 .proto = IPPROTO_ICMPV6, 326 .family = NFPROTO_IPV6, 327 .me = THIS_MODULE, 328 }, 329 }; 330 331 static int __init tcpudp_mt_init(void) 332 { 333 return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); 334 } 335 336 static void __exit tcpudp_mt_exit(void) 337 { 338 xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); 339 } 340 341 module_init(tcpudp_mt_init); 342 module_exit(tcpudp_mt_exit); 343