]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/P2PPacketFilter.c
mDNSResponder-320.5.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / P2PPacketFilter.c
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
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 traffice on the Bonjour port (5353) and the specified port and protocol
228 int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int16_t port, u_int16_t protocol )
229 {
230 int result;
231 u_int32_t 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 service
253 initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, port, protocol );
254
255 result = addRule( devFD, &pr );
256 require( result == 0, exit );
257
258 // allow outbound TCP connections and return traffic for those connections
259 initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath);
260
261 result = addRule( devFD, &pr );
262 require( result == 0, exit );
263
264 result = commitChange( devFD, ticket, anchorPath );
265 require( result == 0, exit );
266
267 exit:
268
269 if( devFD >= 0 )
270 close( devFD );
271
272 return result;
273 }
274
275 int P2PPacketFilterClearBonjourRules()
276 {
277 int result;
278 int pfDev = -1;
279 u_int32_t ticket;
280 char * anchorPath = MDNS_ANCHOR_PATH;
281
282 result = openPFDevice( &pfDev );
283 require( result == 0, exit );
284
285 result = getTicket( pfDev, &ticket, anchorPath );
286 require( result == 0, exit );
287
288 result = commitChange( pfDev, ticket, anchorPath );
289
290 exit:
291
292 if( pfDev >= 0 )
293 close( pfDev );
294
295 return result;
296 }
297