match.c revision 267654
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