linux/net/netfilter/ipset/ip_set_getport.c
<<
>>
Prefs
   1/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License version 2 as
   5 * published by the Free Software Foundation.
   6 */
   7
   8/* Get Layer-4 data from the packets */
   9
  10#include <linux/ip.h>
  11#include <linux/skbuff.h>
  12#include <linux/icmp.h>
  13#include <linux/icmpv6.h>
  14#include <linux/sctp.h>
  15#include <linux/netfilter_ipv6/ip6_tables.h>
  16#include <net/ip.h>
  17#include <net/ipv6.h>
  18
  19#include <linux/netfilter/ipset/ip_set_getport.h>
  20#include <linux/export.h>
  21
  22/* We must handle non-linear skbs */
  23static bool
  24get_port(const struct sk_buff *skb, int protocol, unsigned int protooff,
  25         bool src, __be16 *port, u8 *proto)
  26{
  27        switch (protocol) {
  28        case IPPROTO_TCP: {
  29                struct tcphdr _tcph;
  30                const struct tcphdr *th;
  31
  32                th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph);
  33                if (!th)
  34                        /* No choice either */
  35                        return false;
  36
  37                *port = src ? th->source : th->dest;
  38                break;
  39        }
  40        case IPPROTO_SCTP: {
  41                struct sctphdr _sh;
  42                const struct sctphdr *sh;
  43
  44                sh = skb_header_pointer(skb, protooff, sizeof(_sh), &_sh);
  45                if (!sh)
  46                        /* No choice either */
  47                        return false;
  48
  49                *port = src ? sh->source : sh->dest;
  50                break;
  51        }
  52        case IPPROTO_UDP:
  53        case IPPROTO_UDPLITE: {
  54                struct udphdr _udph;
  55                const struct udphdr *uh;
  56
  57                uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph);
  58                if (!uh)
  59                        /* No choice either */
  60                        return false;
  61
  62                *port = src ? uh->source : uh->dest;
  63                break;
  64        }
  65        case IPPROTO_ICMP: {
  66                struct icmphdr _ich;
  67                const struct icmphdr *ic;
  68
  69                ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich);
  70                if (!ic)
  71                        return false;
  72
  73                *port = (__force __be16)htons((ic->type << 8) | ic->code);
  74                break;
  75        }
  76        case IPPROTO_ICMPV6: {
  77                struct icmp6hdr _ich;
  78                const struct icmp6hdr *ic;
  79
  80                ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich);
  81                if (!ic)
  82                        return false;
  83
  84                *port = (__force __be16)
  85                        htons((ic->icmp6_type << 8) | ic->icmp6_code);
  86                break;
  87        }
  88        default:
  89                break;
  90        }
  91        *proto = protocol;
  92
  93        return true;
  94}
  95
  96bool
  97ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
  98                    __be16 *port, u8 *proto)
  99{
 100        const struct iphdr *iph = ip_hdr(skb);
 101        unsigned int protooff = skb_network_offset(skb) + ip_hdrlen(skb);
 102        int protocol = iph->protocol;
 103
 104        /* See comments at tcp_match in ip_tables.c */
 105        if (protocol <= 0)
 106                return false;
 107
 108        if (ntohs(iph->frag_off) & IP_OFFSET)
 109                switch (protocol) {
 110                case IPPROTO_TCP:
 111                case IPPROTO_SCTP:
 112                case IPPROTO_UDP:
 113                case IPPROTO_UDPLITE:
 114                case IPPROTO_ICMP:
 115                        /* Port info not available for fragment offset > 0 */
 116                        return false;
 117                default:
 118                        /* Other protocols doesn't have ports,
 119                         * so we can match fragments.
 120                         */
 121                        *proto = protocol;
 122                        return true;
 123                }
 124
 125        return get_port(skb, protocol, protooff, src, port, proto);
 126}
 127EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
 128
 129#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
 130bool
 131ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
 132                    __be16 *port, u8 *proto)
 133{
 134        int protoff;
 135        u8 nexthdr;
 136        __be16 frag_off = 0;
 137
 138        nexthdr = ipv6_hdr(skb)->nexthdr;
 139        protoff = ipv6_skip_exthdr(skb,
 140                                   skb_network_offset(skb) +
 141                                        sizeof(struct ipv6hdr), &nexthdr,
 142                                   &frag_off);
 143        if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
 144                return false;
 145
 146        return get_port(skb, nexthdr, protoff, src, port, proto);
 147}
 148EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
 149#endif
 150
 151bool
 152ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
 153{
 154        bool ret;
 155        u8 proto;
 156
 157        switch (pf) {
 158        case NFPROTO_IPV4:
 159                ret = ip_set_get_ip4_port(skb, src, port, &proto);
 160                break;
 161        case NFPROTO_IPV6:
 162                ret = ip_set_get_ip6_port(skb, src, port, &proto);
 163                break;
 164        default:
 165                return false;
 166        }
 167        if (!ret)
 168                return ret;
 169        switch (proto) {
 170        case IPPROTO_TCP:
 171        case IPPROTO_UDP:
 172                return true;
 173        default:
 174                return false;
 175        }
 176}
 177EXPORT_SYMBOL_GPL(ip_set_get_ip_port);
 178