1/* 2 * 3 * Copyright (c) 2011 Apple Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#include <net/if.h> 19#include <System/net/pfvar.h> 20#include <string.h> 21#include <fcntl.h> 22#include <errno.h> 23#include <sys/ioctl.h> 24#include <unistd.h> 25#include <AssertMacros.h> 26#include "P2PPacketFilter.h" 27 28#define AIRDROP_ANCHOR_PATH "com.apple/200.AirDrop" 29#define MDNS_ANCHOR_NAME "Bonjour" 30#define MDNS_ANCHOR_PATH AIRDROP_ANCHOR_PATH "/" MDNS_ANCHOR_NAME 31 32#define PF_DEV_PATH "/dev/pf" 33#define BONJOUR_PORT 5353 34 35static int openPFDevice( int * outFD ) 36{ 37 int err; 38 int fd = open( PF_DEV_PATH, O_RDWR ); 39 40 if( fd >= 0 ) 41 { 42 err = 0; 43 *outFD = fd; 44 } 45 else 46 { 47 err = errno; 48 } 49 50 return err; 51} 52 53static int getTicket( int devFD, u_int32_t * outTicket, char * anchorPath ) 54{ 55 struct pfioc_trans_e trans_e; 56 57 trans_e.rs_num = PF_RULESET_FILTER; 58 strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); 59 60 struct pfioc_trans trans; 61 62 trans.size = 1; 63 trans.esize = sizeof( trans_e ); 64 trans.array = &trans_e; 65 66 int result, ioctlError; 67 68 ioctlError = ioctl( devFD, DIOCXBEGIN, &trans ); 69 if( ioctlError ) 70 { 71 result = errno; 72 } 73 else 74 { 75 result = 0; 76 *outTicket = trans_e.ticket; 77 } 78 79 return result; 80} 81 82static int commitChange( int devFD, u_int32_t ticket, char * anchorPath ) 83{ 84 struct pfioc_trans_e trans_e; 85 86 trans_e.rs_num = PF_RULESET_FILTER; 87 strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); 88 trans_e.ticket = ticket; 89 90 struct pfioc_trans trans; 91 92 trans.size = 1; 93 trans.esize = sizeof( trans_e ); 94 trans.array = &trans_e; 95 96 int result, ioctlError; 97 98 ioctlError = ioctl( devFD, DIOCXCOMMIT, &trans ); 99 if( ioctlError ) 100 result = errno; 101 else 102 result = 0; 103 104 return result; 105} 106 107static int getPoolTicket( int devFD, u_int32_t * outPoolTicket ) 108{ 109 struct pfioc_pooladdr pp; 110 111 int result, ioctlError; 112 113 ioctlError = ioctl( devFD, DIOCBEGINADDRS, &pp ); 114 if( ioctlError ) 115 { 116 result = errno; 117 } 118 else 119 { 120 result = 0; 121 *outPoolTicket = pp.ticket; 122 } 123 124 return result; 125} 126 127static int addRule( int devFD, struct pfioc_rule * pr ) 128{ 129 int result, ioctlResult; 130 131 ioctlResult = ioctl( devFD, DIOCADDRULE, pr ); 132 if( ioctlResult ) 133 result = errno; 134 else 135 result = 0; 136 137 return result; 138} 139 140static void initRuleHeader( struct pfioc_rule * pr, 141 u_int32_t ticket, 142 u_int32_t poolTicket, 143 char * anchorPath ) 144{ 145 pr->action = PF_CHANGE_NONE; 146 pr->ticket = ticket; 147 pr->pool_ticket = poolTicket; 148 strlcpy( pr->anchor, anchorPath, sizeof( pr->anchor ) ); 149} 150 151// allow inbound traffice on the Bonjour port (5353) 152static void initBonjourRule( struct pfioc_rule * pr, 153 const char * interfaceName, 154 u_int32_t ticket, 155 u_int32_t poolTicket, 156 char * anchorPath ) 157{ 158 memset( pr, 0, sizeof( *pr ) ); 159 160 // Header 161 initRuleHeader( pr, ticket, poolTicket, anchorPath ); 162 163 // Rule 164 pr->rule.dst.xport.range.port[0] = htons( BONJOUR_PORT ); 165 pr->rule.dst.xport.range.op = PF_OP_EQ; 166 167 strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); 168 169 pr->rule.action = PF_PASS; 170 pr->rule.direction = PF_IN; 171 pr->rule.keep_state = 1; 172 pr->rule.af = AF_INET6; 173 pr->rule.proto = IPPROTO_UDP; 174 pr->rule.extfilter = PF_EXTFILTER_APD; 175} 176 177// allow outbound TCP connections and return traffic for those connections 178static void initOutboundTCPRule( struct pfioc_rule * pr, 179 const char * interfaceName, 180 u_int32_t ticket, 181 u_int32_t poolTicket, 182 char * anchorPath ) 183{ 184 memset( pr, 0, sizeof( *pr ) ); 185 186 // Header 187 initRuleHeader( pr, ticket, poolTicket, anchorPath ); 188 189 // Rule 190 strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); 191 192 pr->rule.action = PF_PASS; 193 pr->rule.direction = PF_OUT; 194 pr->rule.keep_state = 1; 195 pr->rule.proto = IPPROTO_TCP; 196} 197 198// allow inbound traffic on the specified port and protocol 199static void initPortRule( struct pfioc_rule * pr, 200 const char * interfaceName, 201 u_int32_t ticket, 202 u_int32_t poolTicket, 203 char * anchorPath, 204 u_int16_t port, 205 u_int16_t protocol ) 206{ 207 memset( pr, 0, sizeof( *pr ) ); 208 209 // Header 210 initRuleHeader( pr, ticket, poolTicket, anchorPath ); 211 212 // Rule 213 // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required 214 pr->rule.dst.xport.range.port[0] = port; 215 pr->rule.dst.xport.range.op = PF_OP_EQ; 216 217 strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); 218 219 pr->rule.action = PF_PASS; 220 pr->rule.direction = PF_IN; 221 pr->rule.keep_state = 1; 222 pr->rule.af = AF_INET6; 223 pr->rule.proto = protocol; 224 pr->rule.extfilter = PF_EXTFILTER_APD; 225} 226 227// allow inbound traffic on the Bonjour port (5353) and the specified port and protocol sets 228int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray ) 229{ 230 int result; 231 u_int32_t i, ticket, poolTicket; 232 int devFD = -1; 233 char * anchorPath = MDNS_ANCHOR_PATH; 234 235 result = openPFDevice( &devFD ); 236 require( result == 0, exit ); 237 238 result = getTicket( devFD, &ticket, anchorPath ); 239 require( result == 0, exit ); 240 241 result = getPoolTicket( devFD, &poolTicket ); 242 require( result == 0, exit ); 243 244 struct pfioc_rule pr; 245 246 // allow inbound Bonjour traffice to port 5353 247 initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath); 248 249 result = addRule( devFD, &pr ); 250 require( result == 0, exit ); 251 252 // open inbound port for each service 253 for (i = 0; i < count; i++) { 254 initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, portArray[i], protocolArray[i] ); 255 result = addRule( devFD, &pr ); 256 require( result == 0, exit ); 257 } 258 259 // allow outbound TCP connections and return traffic for those connections 260 initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath); 261 262 result = addRule( devFD, &pr ); 263 require( result == 0, exit ); 264 265 result = commitChange( devFD, ticket, anchorPath ); 266 require( result == 0, exit ); 267 268exit: 269 270 if( devFD >= 0 ) 271 close( devFD ); 272 273 return result; 274} 275 276int P2PPacketFilterClearBonjourRules() 277{ 278 int result; 279 int pfDev = -1; 280 u_int32_t ticket; 281 char * anchorPath = MDNS_ANCHOR_PATH; 282 283 result = openPFDevice( &pfDev ); 284 require( result == 0, exit ); 285 286 result = getTicket( pfDev, &ticket, anchorPath ); 287 require( result == 0, exit ); 288 289 result = commitChange( pfDev, ticket, anchorPath ); 290 291exit: 292 293 if( pfDev >= 0 ) 294 close( pfDev ); 295 296 return result; 297} 298 299