1/*
2  This software is available to you under a choice of one of two
3  licenses.  You may choose to be licensed under the terms of the GNU
4  General Public License (GPL) Version 2, available at
5  <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
6  license, available in the LICENSE.TXT file accompanying this
7  software.  These details are also available at
8  <http://openib.org/license.html>.
9
10  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
13  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
14  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
15  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17  SOFTWARE.
18
19  Copyright (c) 2004 Topspin Communications.  All rights reserved.
20  Copyright (c) 2005-2006 Mellanox Technologies Ltd.  All rights reserved.
21
22  $Id$
23*/
24
25/*
26 * system includes
27 */
28#if HAVE_CONFIG_H
29#  include <config.h>
30#endif /* HAVE_CONFIG_H */
31
32#include <unistd.h>
33#include <errno.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <fnmatch.h>
38#include <sys/socket.h>
39#include <netinet/in.h>
40#include <arpa/inet.h>
41#include <sys/types.h>
42#ifdef __linux__
43#include <linux/types.h>
44#elif defined(__FreeBSD__)
45#define s6_addr32 __u6_addr.__u6_addr32
46#define __be32 uint32_t
47#endif
48
49/*
50 * SDP specific includes
51 */
52#include "libsdp.h"
53
54/* --------------------------------------------------------------------- */
55/* library static and global variables                                   */
56/* --------------------------------------------------------------------- */
57extern char *program_invocation_name, *program_invocation_short_name;
58
59static void
60get_rule_str(
61	struct use_family_rule *rule,
62	char *buf,
63	size_t len )
64{
65	char addr_buf[MAX_ADDR_STR_LEN];
66	char ports_buf[16];
67	char *target = __sdp_get_family_str( rule->target_family );
68	char *prog = rule->prog_name_expr;
69
70	/* TODO: handle IPv6 in rule */
71	if ( rule->match_by_addr ) {
72		char tmp[INET6_ADDRSTRLEN] = "BAD ADDRESS";
73
74		if (rule->ip.ss_family == AF_INET)
75			inet_ntop(AF_INET, &((struct sockaddr_in *)&rule->ip)->sin_addr, tmp, sizeof(tmp));
76		else if (rule->ip.ss_family == AF_INET6)
77			inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&rule->ip)->sin6_addr, tmp, sizeof(tmp));
78
79		sprintf( addr_buf, "%s/%d", tmp, rule->prefixlen);
80	} else {
81		strcpy( addr_buf, "*" );
82	}
83
84	if ( rule->match_by_port )
85		if ( rule->eport > rule->sport )
86			sprintf( ports_buf, "%d", rule->sport );
87		else
88			sprintf( ports_buf, "%d-%d", rule->sport, rule->eport );
89	else
90		sprintf( ports_buf, "*" );
91
92	snprintf( buf, len, "use %s %s %s:%s", target, prog, addr_buf, ports_buf );
93}
94
95static inline int __ipv6_prefix_equal(const __be32 *a1, const __be32 *a2,
96				      unsigned int prefixlen)
97{
98	unsigned pdw, pbi;
99
100	/* check complete u32 in prefix */
101	pdw = prefixlen >> 5;
102	if (pdw && memcmp(a1, a2, pdw << 2))
103		return 0;
104
105	/* check incomplete u32 in prefix */
106	pbi = prefixlen & 0x1f;
107	if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
108		return 0;
109
110	return 1;
111}
112
113static inline int ipv6_prefix_equal(const struct in6_addr *a1,
114				    const struct in6_addr *a2,
115				    unsigned int prefixlen)
116{
117	return __ipv6_prefix_equal(a1->s6_addr32, a2->s6_addr32,
118				   prefixlen);
119}
120
121/* return 0 if the addresses match */
122static inline int
123match_addr(
124	struct use_family_rule *rule,
125	const struct sockaddr *addr_in )
126{
127	const struct sockaddr_in *sin = ( const struct sockaddr_in * )addr_in;
128	const struct sockaddr_in6 *sin6 = ( const struct sockaddr_in6 * )addr_in;
129	const struct sockaddr_in *rule_sin = ( const struct sockaddr_in * )(&rule->ip);
130	const struct sockaddr_in6 *rule_sin6 = ( const struct sockaddr_in6 * )(&rule->ip);
131
132	if (rule_sin->sin_family == AF_INET && !rule_sin->sin_addr.s_addr)
133		return 0;
134
135	if (addr_in->sa_family != rule->ip.ss_family)
136		return -1;
137
138	if (addr_in->sa_family == AF_INET) {
139		return ( rule_sin->sin_addr.s_addr !=
140				( sin->sin_addr.s_addr &
141				 htonl( SDP_NETMASK( rule->prefixlen ) ) ) );
142	}
143
144	/* IPv6 */
145	return !ipv6_prefix_equal(&sin6->sin6_addr, &rule_sin6->sin6_addr, rule->prefixlen);
146}
147
148static int
149match_ip_addr_and_port(
150	struct use_family_rule *rule,
151	const struct sockaddr *addr_in,
152	const socklen_t addrlen )
153{
154	const struct sockaddr_in *sin = ( const struct sockaddr_in * )addr_in;
155	const struct sockaddr_in6 *sin6 = ( const struct sockaddr_in6 * )addr_in;
156	unsigned short port;
157	int match = 1;
158	char addr_buf[MAX_ADDR_STR_LEN];
159	const char *addr_str;
160	char rule_str[512];
161
162	if ( __sdp_log_get_level(  ) <= 3 ) {
163		if ( sin6->sin6_family == AF_INET6 ) {
164			addr_str = inet_ntop( AF_INET6, ( void * )&( sin6->sin6_addr ),
165					addr_buf, MAX_ADDR_STR_LEN );
166			port = ntohs( sin6->sin6_port );
167		} else {
168			addr_str = inet_ntop( AF_INET, ( void * )&( sin->sin_addr ),
169					addr_buf, MAX_ADDR_STR_LEN );
170			port = ntohs( sin->sin_port );
171		}
172		if ( addr_str == NULL )
173			addr_str = "INVALID_ADDR";
174
175		get_rule_str( rule, rule_str, sizeof( rule_str ) );
176
177		__sdp_log( 3, "MATCH: matching %s:%d to %s => \n", addr_str, port,
178					  rule_str );
179	}
180
181	if ( rule->match_by_port ) {
182		port = ntohs( sin->sin_port );
183
184		if ( ( port < rule->sport ) || ( port > rule->eport ) ) {
185			__sdp_log( 3, "NEGATIVE by port range\n" );
186			match = 0;
187		}
188	}
189
190	if ( match && rule->match_by_addr ) {
191		if ( match_addr( rule, addr_in ) ) {
192			__sdp_log( 3, "NEGATIVE by address\n" );
193			match = 0;
194		}
195	}
196
197	if ( match )
198		__sdp_log( 3, "POSITIVE\n" );
199
200	return match;
201}
202
203/* return 1 on match */
204static int
205match_program_name(
206	struct use_family_rule *rule )
207{
208	return !fnmatch( rule->prog_name_expr, program_invocation_short_name, 0 );
209}
210
211static use_family_t
212get_family_by_first_matching_rule(
213	const struct sockaddr *sin,
214	const socklen_t addrlen,
215	struct use_family_rule *rules )
216{
217	struct use_family_rule *rule;
218
219	for ( rule = rules; rule != NULL; rule = rule->next ) {
220		/* skip if not our program */
221		if ( !match_program_name( rule ) )
222			continue;
223
224		/* first rule wins */
225		if ( match_ip_addr_and_port( rule, sin, addrlen ) )
226			return ( rule->target_family );
227	}
228
229	return ( USE_BOTH );
230}
231
232/* return the result of the first matching rule found */
233use_family_t
234__sdp_match_listen(
235	const struct sockaddr * sin,
236	const socklen_t addrlen )
237{
238	use_family_t target_family;
239
240	/* if we do not have any rules we use sdp */
241	if ( __sdp_config_empty(  ) )
242		target_family = USE_SDP;
243	else
244		target_family =
245			get_family_by_first_matching_rule( sin, addrlen,
246														  __sdp_servers_family_rules_head );
247
248	__sdp_log( 4, "MATCH LISTEN: => %s\n",
249				  __sdp_get_family_str( target_family ) );
250
251	return ( target_family );
252}
253
254use_family_t
255__sdp_match_connect(
256	const struct sockaddr * sin,
257	const socklen_t addrlen )
258{
259	use_family_t target_family;
260
261	/* if we do not have any rules we use sdp */
262	if ( __sdp_config_empty(  ) )
263		target_family = USE_SDP;
264	else
265		target_family =
266			get_family_by_first_matching_rule( sin, addrlen,
267														  __sdp_clients_family_rules_head );
268
269	__sdp_log( 4, "MATCH CONNECT: => %s\n",
270				  __sdp_get_family_str( target_family ) );
271
272	return ( target_family );
273}
274
275/* given a set of rules see if there is a global match for current program */
276static use_family_t
277match_by_all_rules_program(
278	struct use_family_rule *rules )
279{
280	int any_sdp = 0;
281	int any_tcp = 0;
282	use_family_t target_family = USE_BOTH;
283	struct use_family_rule *rule;
284
285	for ( rule = rules; ( rule != NULL ) && ( target_family == USE_BOTH );
286			rule = rule->next ) {
287		/* skip if not our program */
288		if ( !match_program_name( rule ) )
289			continue;
290
291		/*
292		 * to declare a dont care we either have a dont care address and port
293		 * or the previous non global rules use the same target family as the
294		 * global rule
295		 */
296		if ( rule->match_by_addr || rule->match_by_port ) {
297			/* not a glocal match rule - just track the target family */
298			if ( rule->target_family == USE_SDP )
299				any_sdp++;
300			else if ( rule->target_family == USE_TCP )
301				any_tcp++;
302		} else {
303			/* a global match so we can declare a match by program */
304			if ( ( rule->target_family == USE_SDP ) && ( any_tcp == 0 ) )
305				target_family = USE_SDP;
306			else if ( ( rule->target_family == USE_TCP ) && ( any_sdp == 0 ) )
307				target_family = USE_TCP;
308		}
309	}
310	return ( target_family );
311}
312
313/* return tcp or sdp if the port and role are dont cares */
314use_family_t
315__sdp_match_by_program(
316	 )
317{
318	use_family_t server_target_family;
319	use_family_t client_target_family;
320	use_family_t target_family = USE_BOTH;
321
322	if ( __sdp_config_empty(  ) ) {
323		target_family = USE_SDP;
324	} else {
325		/* need to try both server and client rules */
326		server_target_family =
327			match_by_all_rules_program( __sdp_servers_family_rules_head );
328		client_target_family =
329			match_by_all_rules_program( __sdp_clients_family_rules_head );
330
331		/* only if both agree */
332		if ( server_target_family == client_target_family )
333			target_family = server_target_family;
334	}
335
336	__sdp_log( 4, "MATCH PROGRAM: => %s\n",
337				  __sdp_get_family_str( target_family ) );
338
339	return ( target_family );
340}
341