linux/net/netfilter/xt_tcpudp.c
<<
>>
Prefs
   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
  14MODULE_DESCRIPTION("Xtables: TCP, UDP and UDP-Lite match");
  15MODULE_LICENSE("GPL");
  16MODULE_ALIAS("xt_tcp");
  17MODULE_ALIAS("xt_udp");
  18MODULE_ALIAS("ipt_udp");
  19MODULE_ALIAS("ipt_tcp");
  20MODULE_ALIAS("ip6t_udp");
  21MODULE_ALIAS("ip6t_tcp");
  22
  23/* Returns 1 if the port is matched by the range, 0 otherwise */
  24static inline bool
  25port_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
  30static bool
  31tcp_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
  65static 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
 120static 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
 128static 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
 155static 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
 163static 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
 220static int __init tcpudp_mt_init(void)
 221{
 222        return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
 223}
 224
 225static void __exit tcpudp_mt_exit(void)
 226{
 227        xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
 228}
 229
 230module_init(tcpudp_mt_init);
 231module_exit(tcpudp_mt_exit);
 232