Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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