]>
Commit | Line | Data |
---|---|---|
9f221bca | 1 | /* -*- Mode: C; tab-width: 4 -*- |
294beb6e A |
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 | ||
83fb1e36 A |
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 | |
294beb6e A |
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 ); | |
83fb1e36 | 39 | |
294beb6e A |
40 | if( fd >= 0 ) |
41 | { | |
42 | err = 0; | |
43 | *outFD = fd; | |
44 | } | |
45 | else | |
46 | { | |
47 | err = errno; | |
48 | } | |
83fb1e36 | 49 | |
294beb6e A |
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; | |
83fb1e36 | 56 | |
294beb6e A |
57 | trans_e.rs_num = PF_RULESET_FILTER; |
58 | strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); | |
83fb1e36 | 59 | |
294beb6e | 60 | struct pfioc_trans trans; |
83fb1e36 | 61 | |
294beb6e A |
62 | trans.size = 1; |
63 | trans.esize = sizeof( trans_e ); | |
64 | trans.array = &trans_e; | |
83fb1e36 | 65 | |
294beb6e | 66 | int result, ioctlError; |
83fb1e36 | 67 | |
294beb6e A |
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 | } | |
83fb1e36 | 78 | |
294beb6e A |
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; | |
83fb1e36 | 85 | |
294beb6e A |
86 | trans_e.rs_num = PF_RULESET_FILTER; |
87 | strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); | |
88 | trans_e.ticket = ticket; | |
83fb1e36 | 89 | |
294beb6e | 90 | struct pfioc_trans trans; |
83fb1e36 | 91 | |
294beb6e A |
92 | trans.size = 1; |
93 | trans.esize = sizeof( trans_e ); | |
94 | trans.array = &trans_e; | |
83fb1e36 | 95 | |
294beb6e | 96 | int result, ioctlError; |
83fb1e36 | 97 | |
294beb6e A |
98 | ioctlError = ioctl( devFD, DIOCXCOMMIT, &trans ); |
99 | if( ioctlError ) | |
100 | result = errno; | |
101 | else | |
102 | result = 0; | |
83fb1e36 | 103 | |
294beb6e A |
104 | return result; |
105 | } | |
106 | ||
107 | static int getPoolTicket( int devFD, u_int32_t * outPoolTicket ) | |
108 | { | |
109 | struct pfioc_pooladdr pp; | |
83fb1e36 | 110 | |
294beb6e | 111 | int result, ioctlError; |
83fb1e36 | 112 | |
294beb6e A |
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 | } | |
83fb1e36 | 123 | |
294beb6e A |
124 | return result; |
125 | } | |
126 | ||
127 | static int addRule( int devFD, struct pfioc_rule * pr ) | |
128 | { | |
129 | int result, ioctlResult; | |
83fb1e36 | 130 | |
294beb6e A |
131 | ioctlResult = ioctl( devFD, DIOCADDRULE, pr ); |
132 | if( ioctlResult ) | |
133 | result = errno; | |
134 | else | |
135 | result = 0; | |
83fb1e36 | 136 | |
294beb6e A |
137 | return result; |
138 | } | |
139 | ||
140 | static void initRuleHeader( struct pfioc_rule * pr, | |
83fb1e36 A |
141 | u_int32_t ticket, |
142 | u_int32_t poolTicket, | |
143 | char * anchorPath ) | |
294beb6e A |
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 | ||
83fb1e36 | 151 | // allow inbound traffice on the Bonjour port (5353) |
294beb6e | 152 | static void initBonjourRule( struct pfioc_rule * pr, |
83fb1e36 A |
153 | const char * interfaceName, |
154 | u_int32_t ticket, | |
155 | u_int32_t poolTicket, | |
156 | char * anchorPath ) | |
294beb6e A |
157 | { |
158 | memset( pr, 0, sizeof( *pr ) ); | |
83fb1e36 | 159 | |
294beb6e A |
160 | // Header |
161 | initRuleHeader( pr, ticket, poolTicket, anchorPath ); | |
83fb1e36 | 162 | |
294beb6e A |
163 | // Rule |
164 | pr->rule.dst.xport.range.port[0] = htons( BONJOUR_PORT ); | |
165 | pr->rule.dst.xport.range.op = PF_OP_EQ; | |
83fb1e36 | 166 | |
294beb6e | 167 | strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); |
83fb1e36 | 168 | |
294beb6e A |
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, | |
83fb1e36 A |
179 | const char * interfaceName, |
180 | u_int32_t ticket, | |
181 | u_int32_t poolTicket, | |
182 | char * anchorPath ) | |
294beb6e A |
183 | { |
184 | memset( pr, 0, sizeof( *pr ) ); | |
83fb1e36 | 185 | |
294beb6e A |
186 | // Header |
187 | initRuleHeader( pr, ticket, poolTicket, anchorPath ); | |
83fb1e36 | 188 | |
294beb6e A |
189 | // Rule |
190 | strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); | |
83fb1e36 | 191 | |
294beb6e A |
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, | |
83fb1e36 A |
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 ) | |
294beb6e A |
206 | { |
207 | memset( pr, 0, sizeof( *pr ) ); | |
83fb1e36 | 208 | |
294beb6e A |
209 | // Header |
210 | initRuleHeader( pr, ticket, poolTicket, anchorPath ); | |
83fb1e36 | 211 | |
294beb6e | 212 | // Rule |
83fb1e36 | 213 | // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required |
294beb6e A |
214 | pr->rule.dst.xport.range.port[0] = port; |
215 | pr->rule.dst.xport.range.op = PF_OP_EQ; | |
83fb1e36 | 216 | |
294beb6e | 217 | strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); |
83fb1e36 | 218 | |
294beb6e A |
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 | ||
83fb1e36 A |
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 ) | |
294beb6e A |
229 | { |
230 | int result; | |
9f221bca A |
231 | u_int32_t i; |
232 | u_int32_t ticket = 0; | |
233 | u_int32_t poolTicket = 0; | |
294beb6e | 234 | int devFD = -1; |
83fb1e36 A |
235 | char * anchorPath = MDNS_ANCHOR_PATH; |
236 | ||
294beb6e A |
237 | result = openPFDevice( &devFD ); |
238 | require( result == 0, exit ); | |
83fb1e36 | 239 | |
294beb6e A |
240 | result = getTicket( devFD, &ticket, anchorPath ); |
241 | require( result == 0, exit ); | |
83fb1e36 | 242 | |
294beb6e A |
243 | result = getPoolTicket( devFD, &poolTicket ); |
244 | require( result == 0, exit ); | |
83fb1e36 | 245 | |
294beb6e | 246 | struct pfioc_rule pr; |
83fb1e36 A |
247 | |
248 | // allow inbound Bonjour traffice to port 5353 | |
294beb6e | 249 | initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath); |
83fb1e36 | 250 | |
294beb6e A |
251 | result = addRule( devFD, &pr ); |
252 | require( result == 0, exit ); | |
83fb1e36 A |
253 | |
254 | // open inbound port for each service | |
9f221bca A |
255 | for (i = 0; i < count; i++) |
256 | { | |
83fb1e36 A |
257 | initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, portArray[i], protocolArray[i] ); |
258 | result = addRule( devFD, &pr ); | |
259 | require( result == 0, exit ); | |
260 | } | |
261 | ||
294beb6e A |
262 | // allow outbound TCP connections and return traffic for those connections |
263 | initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath); | |
83fb1e36 | 264 | |
294beb6e A |
265 | result = addRule( devFD, &pr ); |
266 | require( result == 0, exit ); | |
83fb1e36 | 267 | |
294beb6e A |
268 | result = commitChange( devFD, ticket, anchorPath ); |
269 | require( result == 0, exit ); | |
83fb1e36 | 270 | |
294beb6e | 271 | exit: |
83fb1e36 | 272 | |
294beb6e A |
273 | if( devFD >= 0 ) |
274 | close( devFD ); | |
83fb1e36 | 275 | |
294beb6e A |
276 | return result; |
277 | } | |
278 | ||
279 | int P2PPacketFilterClearBonjourRules() | |
280 | { | |
83fb1e36 A |
281 | int result; |
282 | int pfDev = -1; | |
9f221bca | 283 | u_int32_t ticket = 0; |
83fb1e36 A |
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 | ||
294beb6e | 294 | exit: |
294beb6e | 295 | |
83fb1e36 A |
296 | if( pfDev >= 0 ) |
297 | close( pfDev ); | |
298 | ||
299 | return result; | |
294beb6e A |
300 | } |
301 |