Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ip_set_getport.c
Go to the documentation of this file.
1 /* Copyright (C) 2003-2011 Jozsef Kadlecsik <[email protected]>
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 
20 #include <linux/export.h>
21 
22 /* We must handle non-linear skbs */
23 static bool
24 get_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 == NULL)
34  /* No choice either */
35  return false;
36 
37  *port = src ? th->source : th->dest;
38  break;
39  }
40  case IPPROTO_SCTP: {
41  sctp_sctphdr_t _sh;
42  const sctp_sctphdr_t *sh;
43 
44  sh = skb_header_pointer(skb, protooff, sizeof(_sh), &_sh);
45  if (sh == NULL)
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 == NULL)
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 == NULL)
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 == NULL)
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 
96 bool
97 ip_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 = ip_hdrlen(skb);
102  int protocol = iph->protocol;
103 
104  /* See comments at tcp_match in ip_tables.c */
105  if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET))
106  return false;
107 
108  return get_port(skb, protocol, protooff, src, port, proto);
109 }
111 
112 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
113 bool
114 ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
115  __be16 *port, u8 *proto)
116 {
117  int protoff;
118  u8 nexthdr;
120 
121  nexthdr = ipv6_hdr(skb)->nexthdr;
122  protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
123  &frag_off);
124  if (protoff < 0)
125  return false;
126 
127  return get_port(skb, nexthdr, protoff, src, port, proto);
128 }
129 EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
130 #endif
131 
132 bool
133 ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
134 {
135  bool ret;
136  u8 proto;
137 
138  switch (pf) {
139  case NFPROTO_IPV4:
140  ret = ip_set_get_ip4_port(skb, src, port, &proto);
141  break;
142  case NFPROTO_IPV6:
143  ret = ip_set_get_ip6_port(skb, src, port, &proto);
144  break;
145  default:
146  return false;
147  }
148  if (!ret)
149  return ret;
150  switch (proto) {
151  case IPPROTO_TCP:
152  case IPPROTO_UDP:
153  return true;
154  default:
155  return false;
156  }
157 }