0001
0002 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0003 #include <linux/types.h>
0004 #include <linux/module.h>
0005 #include <net/ip.h>
0006 #include <linux/ipv6.h>
0007 #include <net/ipv6.h>
0008 #include <net/tcp.h>
0009 #include <net/udp.h>
0010 #include <linux/netfilter/x_tables.h>
0011 #include <linux/netfilter/xt_tcpudp.h>
0012 #include <linux/netfilter_ipv4/ip_tables.h>
0013 #include <linux/netfilter_ipv6/ip6_tables.h>
0014
0015 MODULE_DESCRIPTION("Xtables: TCP, UDP and UDP-Lite match");
0016 MODULE_LICENSE("GPL");
0017 MODULE_ALIAS("xt_tcp");
0018 MODULE_ALIAS("xt_udp");
0019 MODULE_ALIAS("ipt_udp");
0020 MODULE_ALIAS("ipt_tcp");
0021 MODULE_ALIAS("ip6t_udp");
0022 MODULE_ALIAS("ip6t_tcp");
0023
0024
0025 static inline bool
0026 port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert)
0027 {
0028 return (port >= min && port <= max) ^ invert;
0029 }
0030
0031 static bool
0032 tcp_find_option(u_int8_t option,
0033 const struct sk_buff *skb,
0034 unsigned int protoff,
0035 unsigned int optlen,
0036 bool invert,
0037 bool *hotdrop)
0038 {
0039
0040 const u_int8_t *op;
0041 u_int8_t _opt[60 - sizeof(struct tcphdr)];
0042 unsigned int i;
0043
0044 pr_debug("finding option\n");
0045
0046 if (!optlen)
0047 return invert;
0048
0049
0050 op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr),
0051 optlen, _opt);
0052 if (op == NULL) {
0053 *hotdrop = true;
0054 return false;
0055 }
0056
0057 for (i = 0; i < optlen; ) {
0058 if (op[i] == option) return !invert;
0059 if (op[i] < 2) i++;
0060 else i += op[i+1]?:1;
0061 }
0062
0063 return invert;
0064 }
0065
0066 static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par)
0067 {
0068 const struct tcphdr *th;
0069 struct tcphdr _tcph;
0070 const struct xt_tcp *tcpinfo = par->matchinfo;
0071
0072 if (par->fragoff != 0) {
0073
0074
0075
0076
0077
0078
0079 if (par->fragoff == 1) {
0080 pr_debug("Dropping evil TCP offset=1 frag.\n");
0081 par->hotdrop = true;
0082 }
0083
0084 return false;
0085 }
0086
0087 th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
0088 if (th == NULL) {
0089
0090
0091 pr_debug("Dropping evil TCP offset=0 tinygram.\n");
0092 par->hotdrop = true;
0093 return false;
0094 }
0095
0096 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
0097 ntohs(th->source),
0098 !!(tcpinfo->invflags & XT_TCP_INV_SRCPT)))
0099 return false;
0100 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
0101 ntohs(th->dest),
0102 !!(tcpinfo->invflags & XT_TCP_INV_DSTPT)))
0103 return false;
0104 if (!NF_INVF(tcpinfo, XT_TCP_INV_FLAGS,
0105 (((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp))
0106 return false;
0107 if (tcpinfo->option) {
0108 if (th->doff * 4 < sizeof(_tcph)) {
0109 par->hotdrop = true;
0110 return false;
0111 }
0112 if (!tcp_find_option(tcpinfo->option, skb, par->thoff,
0113 th->doff*4 - sizeof(_tcph),
0114 tcpinfo->invflags & XT_TCP_INV_OPTION,
0115 &par->hotdrop))
0116 return false;
0117 }
0118 return true;
0119 }
0120
0121 static int tcp_mt_check(const struct xt_mtchk_param *par)
0122 {
0123 const struct xt_tcp *tcpinfo = par->matchinfo;
0124
0125
0126 return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0;
0127 }
0128
0129 static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par)
0130 {
0131 const struct udphdr *uh;
0132 struct udphdr _udph;
0133 const struct xt_udp *udpinfo = par->matchinfo;
0134
0135
0136 if (par->fragoff != 0)
0137 return false;
0138
0139 uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph);
0140 if (uh == NULL) {
0141
0142
0143 pr_debug("Dropping evil UDP tinygram.\n");
0144 par->hotdrop = true;
0145 return false;
0146 }
0147
0148 return port_match(udpinfo->spts[0], udpinfo->spts[1],
0149 ntohs(uh->source),
0150 !!(udpinfo->invflags & XT_UDP_INV_SRCPT))
0151 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
0152 ntohs(uh->dest),
0153 !!(udpinfo->invflags & XT_UDP_INV_DSTPT));
0154 }
0155
0156 static int udp_mt_check(const struct xt_mtchk_param *par)
0157 {
0158 const struct xt_udp *udpinfo = par->matchinfo;
0159
0160
0161 return (udpinfo->invflags & ~XT_UDP_INV_MASK) ? -EINVAL : 0;
0162 }
0163
0164 static struct xt_match tcpudp_mt_reg[] __read_mostly = {
0165 {
0166 .name = "tcp",
0167 .family = NFPROTO_IPV4,
0168 .checkentry = tcp_mt_check,
0169 .match = tcp_mt,
0170 .matchsize = sizeof(struct xt_tcp),
0171 .proto = IPPROTO_TCP,
0172 .me = THIS_MODULE,
0173 },
0174 {
0175 .name = "tcp",
0176 .family = NFPROTO_IPV6,
0177 .checkentry = tcp_mt_check,
0178 .match = tcp_mt,
0179 .matchsize = sizeof(struct xt_tcp),
0180 .proto = IPPROTO_TCP,
0181 .me = THIS_MODULE,
0182 },
0183 {
0184 .name = "udp",
0185 .family = NFPROTO_IPV4,
0186 .checkentry = udp_mt_check,
0187 .match = udp_mt,
0188 .matchsize = sizeof(struct xt_udp),
0189 .proto = IPPROTO_UDP,
0190 .me = THIS_MODULE,
0191 },
0192 {
0193 .name = "udp",
0194 .family = NFPROTO_IPV6,
0195 .checkentry = udp_mt_check,
0196 .match = udp_mt,
0197 .matchsize = sizeof(struct xt_udp),
0198 .proto = IPPROTO_UDP,
0199 .me = THIS_MODULE,
0200 },
0201 {
0202 .name = "udplite",
0203 .family = NFPROTO_IPV4,
0204 .checkentry = udp_mt_check,
0205 .match = udp_mt,
0206 .matchsize = sizeof(struct xt_udp),
0207 .proto = IPPROTO_UDPLITE,
0208 .me = THIS_MODULE,
0209 },
0210 {
0211 .name = "udplite",
0212 .family = NFPROTO_IPV6,
0213 .checkentry = udp_mt_check,
0214 .match = udp_mt,
0215 .matchsize = sizeof(struct xt_udp),
0216 .proto = IPPROTO_UDPLITE,
0217 .me = THIS_MODULE,
0218 },
0219 };
0220
0221 static int __init tcpudp_mt_init(void)
0222 {
0223 return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
0224 }
0225
0226 static void __exit tcpudp_mt_exit(void)
0227 {
0228 xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
0229 }
0230
0231 module_init(tcpudp_mt_init);
0232 module_exit(tcpudp_mt_exit);