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
42/*
43 * SDP specific includes
44 */
45#include "libsdp.h"
46
47/* --------------------------------------------------------------------- */
48/* library static and global variables                                   */
49/* --------------------------------------------------------------------- */
50extern char *program_invocation_name, *program_invocation_short_name;
51
52static void
53get_rule_str(
54	struct use_family_rule *rule,
55	char *buf,
56	size_t len )
57{
58	char addr_buf[MAX_ADDR_STR_LEN];
59	char ports_buf[16];
60	char *target = __sdp_get_family_str( rule->target_family );
61	char *prog = rule->prog_name_expr;
62
63	/* TODO: handle IPv6 in rule */
64	if ( rule->match_by_addr ) {
65		if ( rule->prefixlen != 32 )
66			sprintf( addr_buf, "%s/%d", inet_ntoa( rule->ipv4 ),
67						rule->prefixlen );
68		else
69			sprintf( addr_buf, "%s", inet_ntoa( rule->ipv4 ) );
70	} else {
71		strcpy( addr_buf, "*" );
72	}
73
74	if ( rule->match_by_port )
75		if ( rule->eport > rule->sport )
76			sprintf( ports_buf, "%d", rule->sport );
77		else
78			sprintf( ports_buf, "%d-%d", rule->sport, rule->eport );
79	else
80		sprintf( ports_buf, "*" );
81
82	snprintf( buf, len, "use %s %s %s:%s", target, prog, addr_buf, ports_buf );
83}
84
85/* return 0 if the addresses match */
86static inline int
87match_ipv4_addr(
88	struct use_family_rule *rule,
89	const struct sockaddr_in *sin )
90{
91	return ( rule->ipv4.s_addr !=
92				( sin->sin_addr.
93				  s_addr & htonl( SDP_NETMASK( rule->prefixlen ) ) ) );
94}
95
96static int
97match_ip_addr_and_port(
98	struct use_family_rule *rule,
99	const struct sockaddr *addr_in,
100	const socklen_t addrlen )
101{
102	const struct sockaddr_in *sin = ( const struct sockaddr_in * )addr_in;
103	const struct sockaddr_in6 *sin6 = ( const struct sockaddr_in6 * )addr_in;
104	struct sockaddr_in tmp_sin;
105	unsigned short port;
106	int match = 1;
107	char addr_buf[MAX_ADDR_STR_LEN];
108	const char *addr_str;
109	char rule_str[512];
110
111	if ( __sdp_log_get_level(  ) <= 3 ) {
112		if ( sin6->sin6_family == AF_INET6 ) {
113			addr_str =
114				inet_ntop( AF_INET6, ( void * )&( sin6->sin6_addr ), addr_buf,
115							  MAX_ADDR_STR_LEN );
116			port = ntohs( sin6->sin6_port );
117		} else {
118			addr_str =
119				inet_ntop( AF_INET, ( void * )&( sin->sin_addr ), addr_buf,
120							  MAX_ADDR_STR_LEN );
121			port = ntohs( sin->sin_port );
122		}
123		if ( addr_str == NULL )
124			addr_str = "INVALID_ADDR";
125
126		get_rule_str( rule, rule_str, sizeof( rule_str ) );
127
128		__sdp_log( 3, "MATCH: matching %s:%d to %s => \n", addr_str, port,
129					  rule_str );
130	}
131
132	/* We currently only support IPv4 and IPv4 embedded in IPv6 */
133	if ( rule->match_by_port ) {
134		if ( sin6->sin6_family == AF_INET6 )
135			port = ntohs( sin6->sin6_port );
136		else
137			port = ntohs( sin->sin_port );
138
139		if ( ( port < rule->sport ) || ( port > rule->eport ) ) {
140			__sdp_log( 3, "NEGATIVE by port range\n" );
141			match = 0;
142		}
143	}
144
145	if ( match && rule->match_by_addr ) {
146		if ( __sdp_sockaddr_to_sdp( addr_in, addrlen, &tmp_sin, NULL ) ||
147			  match_ipv4_addr( rule, &tmp_sin ) ) {
148			__sdp_log( 3, "NEGATIVE by address\n" );
149			match = 0;
150		}
151	}
152
153	if ( match )
154		__sdp_log( 3, "POSITIVE\n" );
155
156	return match;
157}
158
159/* return 1 on match */
160static int
161match_program_name(
162	struct use_family_rule *rule )
163{
164	return !fnmatch( rule->prog_name_expr, program_invocation_short_name, 0 );
165}
166
167static use_family_t
168get_family_by_first_matching_rule(
169	const struct sockaddr *sin,
170	const socklen_t addrlen,
171	struct use_family_rule *rules )
172{
173	struct use_family_rule *rule;
174
175	for ( rule = rules; rule != NULL; rule = rule->next ) {
176		/* skip if not our program */
177		if ( !match_program_name( rule ) )
178			continue;
179
180		/* first rule wins */
181		if ( match_ip_addr_and_port( rule, sin, addrlen ) )
182			return ( rule->target_family );
183	}
184
185	return ( USE_BOTH );
186}
187
188/* return the result of the first matching rule found */
189use_family_t
190__sdp_match_listen(
191	const struct sockaddr * sin,
192	const socklen_t addrlen )
193{
194	use_family_t target_family;
195
196	/* if we do not have any rules we use sdp */
197	if ( __sdp_config_empty(  ) )
198		target_family = USE_SDP;
199	else
200		target_family =
201			get_family_by_first_matching_rule( sin, addrlen,
202														  __sdp_servers_family_rules_head );
203
204	__sdp_log( 4, "MATCH LISTEN: => %s\n",
205				  __sdp_get_family_str( target_family ) );
206
207	return ( target_family );
208}
209
210use_family_t
211__sdp_match_connect(
212	const struct sockaddr * sin,
213	const socklen_t addrlen )
214{
215	use_family_t target_family;
216
217	/* if we do not have any rules we use sdp */
218	if ( __sdp_config_empty(  ) )
219		target_family = USE_SDP;
220	else
221		target_family =
222			get_family_by_first_matching_rule( sin, addrlen,
223														  __sdp_clients_family_rules_head );
224
225	__sdp_log( 4, "MATCH CONNECT: => %s\n",
226				  __sdp_get_family_str( target_family ) );
227
228	return ( target_family );
229}
230
231/* given a set of rules see if there is a global match for current program */
232static use_family_t
233match_by_all_rules_program(
234	struct use_family_rule *rules )
235{
236	int any_sdp = 0;
237	int any_tcp = 0;
238	use_family_t target_family = USE_BOTH;
239	struct use_family_rule *rule;
240
241	for ( rule = rules; ( rule != NULL ) && ( target_family == USE_BOTH );
242			rule = rule->next ) {
243		/* skip if not our program */
244		if ( !match_program_name( rule ) )
245			continue;
246
247		/*
248		 * to declare a dont care we either have a dont care address and port
249		 * or the previous non global rules use the same target family as the
250		 * global rule
251		 */
252		if ( rule->match_by_addr || rule->match_by_port ) {
253			/* not a glocal match rule - just track the target family */
254			if ( rule->target_family == USE_SDP )
255				any_sdp++;
256			else if ( rule->target_family == USE_TCP )
257				any_tcp++;
258		} else {
259			/* a global match so we can declare a match by program */
260			if ( ( rule->target_family == USE_SDP ) && ( any_tcp == 0 ) )
261				target_family = USE_SDP;
262			else if ( ( rule->target_family == USE_TCP ) && ( any_sdp == 0 ) )
263				target_family = USE_TCP;
264		}
265	}
266	return ( target_family );
267}
268
269/* return tcp or sdp if the port and role are dont cares */
270use_family_t
271__sdp_match_by_program(
272	 )
273{
274	use_family_t server_target_family;
275	use_family_t client_target_family;
276	use_family_t target_family = USE_BOTH;
277
278	if ( __sdp_config_empty(  ) ) {
279		target_family = USE_SDP;
280	} else {
281		/* need to try both server and client rules */
282		server_target_family =
283			match_by_all_rules_program( __sdp_servers_family_rules_head );
284		client_target_family =
285			match_by_all_rules_program( __sdp_clients_family_rules_head );
286
287		/* only if both agree */
288		if ( server_target_family == client_target_family )
289			target_family = server_target_family;
290	}
291
292	__sdp_log( 4, "MATCH PROGRAM: => %s\n",
293				  __sdp_get_family_str( target_family ) );
294
295	return ( target_family );
296}
297