]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/P2PPacketFilter.c
mDNSResponder-1310.80.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / P2PPacketFilter.c
1 /* -*- Mode: C; tab-width: 4 -*-
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
35 static 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
53 static 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
82 static 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
107 static 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
127 static 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
140 static 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)
152 static 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
178 static 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
199 static 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
228 int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray )
229 {
230 int result;
231 u_int32_t i;
232 u_int32_t ticket = 0;
233 u_int32_t poolTicket = 0;
234 int devFD = -1;
235 char * anchorPath = MDNS_ANCHOR_PATH;
236
237 result = openPFDevice( &devFD );
238 require( result == 0, exit );
239
240 result = getTicket( devFD, &ticket, anchorPath );
241 require( result == 0, exit );
242
243 result = getPoolTicket( devFD, &poolTicket );
244 require( result == 0, exit );
245
246 struct pfioc_rule pr;
247
248 // allow inbound Bonjour traffice to port 5353
249 initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath);
250
251 result = addRule( devFD, &pr );
252 require( result == 0, exit );
253
254 // open inbound port for each service
255 for (i = 0; i < count; i++)
256 {
257 initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, portArray[i], protocolArray[i] );
258 result = addRule( devFD, &pr );
259 require( result == 0, exit );
260 }
261
262 // allow outbound TCP connections and return traffic for those connections
263 initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath);
264
265 result = addRule( devFD, &pr );
266 require( result == 0, exit );
267
268 result = commitChange( devFD, ticket, anchorPath );
269 require( result == 0, exit );
270
271 exit:
272
273 if( devFD >= 0 )
274 close( devFD );
275
276 return result;
277 }
278
279 int P2PPacketFilterClearBonjourRules()
280 {
281 int result;
282 int pfDev = -1;
283 u_int32_t ticket = 0;
284 char * anchorPath = MDNS_ANCHOR_PATH;
285
286 result = openPFDevice( &pfDev );
287 require( result == 0, exit );
288
289 result = getTicket( pfDev, &ticket, anchorPath );
290 require( result == 0, exit );
291
292 result = commitChange( pfDev, ticket, anchorPath );
293
294 exit:
295
296 if( pfDev >= 0 )
297 close( pfDev );
298
299 return result;
300 }
301