Commit | Line | Data |
---|---|---|
fe03d474 | 1 | /* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@netfilter.org> |
a7b4f989 JK |
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> | |
91eb7c08 | 14 | #include <linux/sctp.h> |
a7b4f989 JK |
15 | #include <linux/netfilter_ipv6/ip6_tables.h> |
16 | #include <net/ip.h> | |
724bab47 | 17 | #include <net/ipv6.h> |
a7b4f989 JK |
18 | |
19 | #include <linux/netfilter/ipset/ip_set_getport.h> | |
bc3b2d7f | 20 | #include <linux/export.h> |
a7b4f989 JK |
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); | |
ca0f6a5c | 33 | if (!th) |
a7b4f989 JK |
34 | /* No choice either */ |
35 | return false; | |
36 | ||
37 | *port = src ? th->source : th->dest; | |
38 | break; | |
39 | } | |
91eb7c08 | 40 | case IPPROTO_SCTP: { |
ae146d9b XL |
41 | struct sctphdr _sh; |
42 | const struct sctphdr *sh; | |
91eb7c08 JK |
43 | |
44 | sh = skb_header_pointer(skb, protooff, sizeof(_sh), &_sh); | |
ca0f6a5c | 45 | if (!sh) |
91eb7c08 JK |
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: { | |
a7b4f989 JK |
54 | struct udphdr _udph; |
55 | const struct udphdr *uh; | |
56 | ||
57 | uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph); | |
ca0f6a5c | 58 | if (!uh) |
a7b4f989 JK |
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); | |
ca0f6a5c | 70 | if (!ic) |
a7b4f989 JK |
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); | |
ca0f6a5c | 81 | if (!ic) |
a7b4f989 JK |
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); | |
3e4e8d12 | 101 | unsigned int protooff = skb_network_offset(skb) + ip_hdrlen(skb); |
a7b4f989 JK |
102 | int protocol = iph->protocol; |
103 | ||
104 | /* See comments at tcp_match in ip_tables.c */ | |
60b0fe37 | 105 | if (protocol <= 0) |
a7b4f989 JK |
106 | return false; |
107 | ||
60b0fe37 AP |
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, | |
ca0f6a5c JK |
119 | * so we can match fragments. |
120 | */ | |
60b0fe37 AP |
121 | *proto = protocol; |
122 | return true; | |
123 | } | |
124 | ||
a7b4f989 JK |
125 | return get_port(skb, protocol, protooff, src, port, proto); |
126 | } | |
127 | EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); | |
128 | ||
c0cd1156 | 129 | #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
a7b4f989 JK |
130 | bool |
131 | ip_set_get_ip6_port(const struct sk_buff *skb, bool src, | |
132 | __be16 *port, u8 *proto) | |
133 | { | |
724bab47 PM |
134 | int protoff; |
135 | u8 nexthdr; | |
55524c21 | 136 | __be16 frag_off = 0; |
a7b4f989 | 137 | |
724bab47 | 138 | nexthdr = ipv6_hdr(skb)->nexthdr; |
3e4e8d12 AD |
139 | protoff = ipv6_skip_exthdr(skb, |
140 | skb_network_offset(skb) + | |
141 | sizeof(struct ipv6hdr), &nexthdr, | |
75f2811c | 142 | &frag_off); |
55524c21 | 143 | if (protoff < 0 || (frag_off & htons(~0x7)) != 0) |
a7b4f989 JK |
144 | return false; |
145 | ||
724bab47 | 146 | return get_port(skb, nexthdr, protoff, src, port, proto); |
a7b4f989 JK |
147 | } |
148 | EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); | |
724bab47 | 149 | #endif |
a7b4f989 JK |
150 | |
151 | bool | |
152 | ip_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) { | |
c15f1c83 | 158 | case NFPROTO_IPV4: |
a7b4f989 | 159 | ret = ip_set_get_ip4_port(skb, src, port, &proto); |
316ed388 | 160 | break; |
c15f1c83 | 161 | case NFPROTO_IPV6: |
a7b4f989 | 162 | ret = ip_set_get_ip6_port(skb, src, port, &proto); |
316ed388 | 163 | break; |
a7b4f989 JK |
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 | } | |
177 | EXPORT_SYMBOL_GPL(ip_set_get_ip_port); |