ip_set_getport.c revision bc3b2d7fb9b014d75ebb79ba371a763dbab5e8cf
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 == 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
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 = 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}
110EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
111
112#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
113bool
114ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
115		    __be16 *port, u8 *proto)
116{
117	int protoff;
118	u8 nexthdr;
119
120	nexthdr = ipv6_hdr(skb)->nexthdr;
121	protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);
122	if (protoff < 0)
123		return false;
124
125	return get_port(skb, nexthdr, protoff, src, port, proto);
126}
127EXPORT_SYMBOL_GPL(ip_set_get_ip6_port);
128#endif
129
130bool
131ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
132{
133	bool ret;
134	u8 proto;
135
136	switch (pf) {
137	case AF_INET:
138		ret = ip_set_get_ip4_port(skb, src, port, &proto);
139		break;
140	case AF_INET6:
141		ret = ip_set_get_ip6_port(skb, src, port, &proto);
142		break;
143	default:
144		return false;
145	}
146	if (!ret)
147		return ret;
148	switch (proto) {
149	case IPPROTO_TCP:
150	case IPPROTO_UDP:
151		return true;
152	default:
153		return false;
154	}
155}
156EXPORT_SYMBOL_GPL(ip_set_get_ip_port);
157