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