1 #include <linux/types.h> 2 #include <linux/module.h> 3 #include <net/ip.h> 4 #include <linux/ipv6.h> 5 #include <net/ipv6.h> 6 #include <net/tcp.h> 7 #include <net/udp.h> 8 #include <linux/netfilter/x_tables.h> 9 #include <linux/netfilter/xt_tcpudp.h> 10 #include <linux/netfilter_ipv4/ip_tables.h> 11 #include <linux/netfilter_ipv6/ip6_tables.h> 12 13 MODULE_DESCRIPTION("Xtables: TCP, UDP and UDP-Lite match"); 14 MODULE_LICENSE("GPL"); 15 MODULE_ALIAS("xt_tcp"); 16 MODULE_ALIAS("xt_udp"); 17 MODULE_ALIAS("ipt_udp"); 18 MODULE_ALIAS("ipt_tcp"); 19 MODULE_ALIAS("ip6t_udp"); 20 MODULE_ALIAS("ip6t_tcp"); 21 22 #ifdef DEBUG_IP_FIREWALL_USER 23 #define duprintf(format, args...) printk(format , ## args) 24 #else 25 #define duprintf(format, args...) 26 #endif 27 28 29 /* Returns 1 if the port is matched by the range, 0 otherwise */ 30 static inline bool 31 port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert) 32 { 33 return (port >= min && port <= max) ^ invert; 34 } 35 36 static bool 37 tcp_find_option(u_int8_t option, 38 const struct sk_buff *skb, 39 unsigned int protoff, 40 unsigned int optlen, 41 bool invert, 42 bool *hotdrop) 43 { 44 /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ 45 const u_int8_t *op; 46 u_int8_t _opt[60 - sizeof(struct tcphdr)]; 47 unsigned int i; 48 49 duprintf("tcp_match: finding option\n"); 50 51 if (!optlen) 52 return invert; 53 54 /* If we don't have the whole header, drop packet. */ 55 op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr), 56 optlen, _opt); 57 if (op == NULL) { 58 *hotdrop = true; 59 return false; 60 } 61 62 for (i = 0; i < optlen; ) { 63 if (op[i] == option) return !invert; 64 if (op[i] < 2) i++; 65 else i += op[i+1]?:1; 66 } 67 68 return invert; 69 } 70 71 static bool tcp_mt(const struct sk_buff *skb, const struct xt_match_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 duprintf("Dropping evil TCP offset=1 frag.\n"); 86 *par->hotdrop = true; 87 } 88 /* Must not be a fragment. */ 89 return false; 90 } 91 92 #define FWINVTCP(bool, invflg) ((bool) ^ !!(tcpinfo->invflags & (invflg))) 93 94 th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); 95 if (th == NULL) { 96 /* We've been asked to examine this packet, and we 97 can't. Hence, no choice but to drop. */ 98 duprintf("Dropping evil TCP offset=0 tinygram.\n"); 99 *par->hotdrop = true; 100 return false; 101 } 102 103 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1], 104 ntohs(th->source), 105 !!(tcpinfo->invflags & XT_TCP_INV_SRCPT))) 106 return false; 107 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], 108 ntohs(th->dest), 109 !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) 110 return false; 111 if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask) 112 == tcpinfo->flg_cmp, 113 XT_TCP_INV_FLAGS)) 114 return false; 115 if (tcpinfo->option) { 116 if (th->doff * 4 < sizeof(_tcph)) { 117 *par->hotdrop = true; 118 return false; 119 } 120 if (!tcp_find_option(tcpinfo->option, skb, par->thoff, 121 th->doff*4 - sizeof(_tcph), 122 tcpinfo->invflags & XT_TCP_INV_OPTION, 123 par->hotdrop)) 124 return false; 125 } 126 return true; 127 } 128 129 static bool tcp_mt_check(const struct xt_mtchk_param *par) 130 { 131 const struct xt_tcp *tcpinfo = par->matchinfo; 132 133 /* Must specify no unknown invflags */ 134 return !(tcpinfo->invflags & ~XT_TCP_INV_MASK); 135 } 136 137 static bool udp_mt(const struct sk_buff *skb, const struct xt_match_param *par) 138 { 139 const struct udphdr *uh; 140 struct udphdr _udph; 141 const struct xt_udp *udpinfo = par->matchinfo; 142 143 /* Must not be a fragment. */ 144 if (par->fragoff != 0) 145 return false; 146 147 uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph); 148 if (uh == NULL) { 149 /* We've been asked to examine this packet, and we 150 can't. Hence, no choice but to drop. */ 151 duprintf("Dropping evil UDP tinygram.\n"); 152 *par->hotdrop = true; 153 return false; 154 } 155 156 return port_match(udpinfo->spts[0], udpinfo->spts[1], 157 ntohs(uh->source), 158 !!(udpinfo->invflags & XT_UDP_INV_SRCPT)) 159 && port_match(udpinfo->dpts[0], udpinfo->dpts[1], 160 ntohs(uh->dest), 161 !!(udpinfo->invflags & XT_UDP_INV_DSTPT)); 162 } 163 164 static bool udp_mt_check(const struct xt_mtchk_param *par) 165 { 166 const struct xt_udp *udpinfo = par->matchinfo; 167 168 /* Must specify no unknown invflags */ 169 return !(udpinfo->invflags & ~XT_UDP_INV_MASK); 170 } 171 172 static struct xt_match tcpudp_mt_reg[] __read_mostly = { 173 { 174 .name = "tcp", 175 .family = NFPROTO_IPV4, 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 = "tcp", 184 .family = NFPROTO_IPV6, 185 .checkentry = tcp_mt_check, 186 .match = tcp_mt, 187 .matchsize = sizeof(struct xt_tcp), 188 .proto = IPPROTO_TCP, 189 .me = THIS_MODULE, 190 }, 191 { 192 .name = "udp", 193 .family = NFPROTO_IPV4, 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 = "udp", 202 .family = NFPROTO_IPV6, 203 .checkentry = udp_mt_check, 204 .match = udp_mt, 205 .matchsize = sizeof(struct xt_udp), 206 .proto = IPPROTO_UDP, 207 .me = THIS_MODULE, 208 }, 209 { 210 .name = "udplite", 211 .family = NFPROTO_IPV4, 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 .name = "udplite", 220 .family = NFPROTO_IPV6, 221 .checkentry = udp_mt_check, 222 .match = udp_mt, 223 .matchsize = sizeof(struct xt_udp), 224 .proto = IPPROTO_UDPLITE, 225 .me = THIS_MODULE, 226 }, 227 }; 228 229 static int __init tcpudp_mt_init(void) 230 { 231 return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); 232 } 233 234 static void __exit tcpudp_mt_exit(void) 235 { 236 xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); 237 } 238 239 module_init(tcpudp_mt_init); 240 module_exit(tcpudp_mt_exit); 241
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.