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