]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ofed/libsdp/src/match.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ofed / libsdp / src / match.c
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 /* --------------------------------------------------------------------- */
50 extern char *program_invocation_name, *program_invocation_short_name;
51
52 static void
53 get_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 */
86 static inline int
87 match_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
96 static int
97 match_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 */
160 static int
161 match_program_name(
162         struct use_family_rule *rule )
163 {
164         return !fnmatch( rule->prog_name_expr, program_invocation_short_name, 0 );
165 }
166
167 static use_family_t
168 get_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 */
189 use_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
210 use_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 */
232 static use_family_t
233 match_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 */
270 use_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 }