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