]>
Commit | Line | Data |
---|---|---|
7ba0088d A |
1 | /* |
2 | * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
b7080c8e A |
22 | /* |
23 | * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp | |
24 | * Copyright (c) 1994 Ugen J.S.Antsilevich | |
25 | * | |
26 | * Idea and grammar partially left from: | |
27 | * Copyright (c) 1993 Daniel Boulet | |
28 | * | |
29 | * Redistribution and use in source forms, with and without modification, | |
30 | * are permitted provided that this entire comment appears intact. | |
31 | * | |
32 | * Redistribution in binary form may occur without any restrictions. | |
33 | * Obviously, it would be nice if you gave credit where credit is due | |
34 | * but requiring it would be too onerous. | |
35 | * | |
36 | * This software is provided ``AS IS'' without any warranties of any kind. | |
37 | * | |
38 | * NEW command line interface for IP firewall facility | |
39 | * | |
b7080c8e A |
40 | */ |
41 | ||
7ba0088d A |
42 | |
43 | ||
44 | #include <sys/param.h> | |
45 | #include <sys/mbuf.h> | |
b7080c8e A |
46 | #include <sys/socket.h> |
47 | #include <sys/sockio.h> | |
7ba0088d | 48 | #include <sys/sysctl.h> |
b7080c8e A |
49 | #include <sys/time.h> |
50 | #include <sys/wait.h> | |
7ba0088d | 51 | #include <sys/ioctl.h> |
b7080c8e A |
52 | |
53 | #include <ctype.h> | |
54 | #include <err.h> | |
55 | #include <errno.h> | |
7ba0088d | 56 | #include <grp.h> |
b7080c8e A |
57 | #include <limits.h> |
58 | #include <netdb.h> | |
7ba0088d | 59 | #include <pwd.h> |
b7080c8e A |
60 | #include <signal.h> |
61 | #include <stdio.h> | |
62 | #include <stdlib.h> | |
63 | #include <stdarg.h> | |
64 | #include <string.h> | |
b7080c8e | 65 | #include <unistd.h> |
7ba0088d | 66 | #include <sysexits.h> |
b7080c8e A |
67 | |
68 | #include <net/if.h> | |
69 | #include <netinet/in.h> | |
70 | #include <netinet/in_systm.h> | |
b7080c8e A |
71 | #include <netinet/ip.h> |
72 | #include <netinet/ip_icmp.h> | |
73 | #include <netinet/ip_fw.h> | |
74 | #include <net/route.h> /* def. of struct route */ | |
7ba0088d | 75 | #ifdef DUMMYNET |
b7080c8e | 76 | #include <netinet/ip_dummynet.h> |
7ba0088d | 77 | #endif /* DUMMYNET */ |
b7080c8e A |
78 | #include <netinet/tcp.h> |
79 | #include <arpa/inet.h> | |
80 | ||
7ba0088d A |
81 | int s, /* main RAW socket */ |
82 | do_resolv, /* Would try to resolve all */ | |
83 | do_acct, /* Show packet/byte count */ | |
84 | do_time, /* Show time stamps */ | |
85 | do_quiet, /* Be quiet in add and flush */ | |
86 | do_force, /* Don't ask for confirmation */ | |
87 | #ifdef DUMMYNET | |
88 | do_pipe, /* this cmd refers to a pipe */ | |
89 | #endif /* DUMMYNET */ | |
90 | do_sort, /* field to sort results (0 = no) */ | |
91 | verbose; | |
b7080c8e A |
92 | |
93 | struct icmpcode { | |
94 | int code; | |
95 | char *str; | |
96 | }; | |
97 | ||
98 | static struct icmpcode icmpcodes[] = { | |
99 | { ICMP_UNREACH_NET, "net" }, | |
100 | { ICMP_UNREACH_HOST, "host" }, | |
101 | { ICMP_UNREACH_PROTOCOL, "protocol" }, | |
102 | { ICMP_UNREACH_PORT, "port" }, | |
103 | { ICMP_UNREACH_NEEDFRAG, "needfrag" }, | |
104 | { ICMP_UNREACH_SRCFAIL, "srcfail" }, | |
105 | { ICMP_UNREACH_NET_UNKNOWN, "net-unknown" }, | |
106 | { ICMP_UNREACH_HOST_UNKNOWN, "host-unknown" }, | |
107 | { ICMP_UNREACH_ISOLATED, "isolated" }, | |
108 | { ICMP_UNREACH_NET_PROHIB, "net-prohib" }, | |
109 | { ICMP_UNREACH_HOST_PROHIB, "host-prohib" }, | |
110 | { ICMP_UNREACH_TOSNET, "tosnet" }, | |
111 | { ICMP_UNREACH_TOSHOST, "toshost" }, | |
112 | { ICMP_UNREACH_FILTER_PROHIB, "filter-prohib" }, | |
113 | { ICMP_UNREACH_HOST_PRECEDENCE, "host-precedence" }, | |
7ba0088d | 114 | { ICMP_UNREACH_PRECEDENCE_CUTOFF, "precedence-cutoff" }, |
b7080c8e A |
115 | { 0, NULL } |
116 | }; | |
117 | ||
118 | static void show_usage(const char *fmt, ...); | |
119 | ||
120 | static int | |
121 | mask_bits(struct in_addr m_ad) | |
122 | { | |
7ba0088d | 123 | int h_fnd = 0, h_num = 0, i; |
b7080c8e A |
124 | u_long mask; |
125 | ||
7ba0088d A |
126 | mask = ntohl(m_ad.s_addr); |
127 | for (i = 0; i < sizeof(u_long) * CHAR_BIT; i++) { | |
b7080c8e | 128 | if (mask & 1L) { |
7ba0088d | 129 | h_fnd = 1; |
b7080c8e A |
130 | h_num++; |
131 | } else { | |
132 | if (h_fnd) | |
133 | return -1; | |
134 | } | |
7ba0088d | 135 | mask = mask >> 1; |
b7080c8e A |
136 | } |
137 | return h_num; | |
7ba0088d | 138 | } |
b7080c8e A |
139 | |
140 | static void | |
141 | print_port(prot, port, comma) | |
142 | u_char prot; | |
143 | u_short port; | |
144 | const char *comma; | |
145 | { | |
146 | struct servent *se; | |
147 | struct protoent *pe; | |
148 | const char *protocol; | |
149 | int printed = 0; | |
150 | ||
7ba0088d A |
151 | if (!strcmp(comma, ":")) { |
152 | printf("%s0x%04x", comma, port); | |
153 | return; | |
154 | } | |
b7080c8e A |
155 | if (do_resolv) { |
156 | pe = getprotobynumber(prot); | |
157 | if (pe) | |
158 | protocol = pe->p_name; | |
159 | else | |
160 | protocol = NULL; | |
161 | ||
162 | se = getservbyport(htons(port), protocol); | |
163 | if (se) { | |
164 | printf("%s%s", comma, se->s_name); | |
165 | printed = 1; | |
166 | } | |
7ba0088d | 167 | } |
b7080c8e | 168 | if (!printed) |
7ba0088d | 169 | printf("%s%d", comma, port); |
b7080c8e A |
170 | } |
171 | ||
172 | static void | |
173 | print_iface(char *key, union ip_fw_if *un, int byname) | |
174 | { | |
175 | char ifnb[FW_IFNLEN+1]; | |
176 | ||
177 | if (byname) { | |
178 | strncpy(ifnb, un->fu_via_if.name, FW_IFNLEN); | |
7ba0088d | 179 | ifnb[FW_IFNLEN] = '\0'; |
b7080c8e A |
180 | if (un->fu_via_if.unit == -1) |
181 | printf(" %s %s*", key, ifnb); | |
7ba0088d | 182 | else |
b7080c8e A |
183 | printf(" %s %s%d", key, ifnb, un->fu_via_if.unit); |
184 | } else if (un->fu_via_ip.s_addr != 0) { | |
185 | printf(" %s %s", key, inet_ntoa(un->fu_via_ip)); | |
186 | } else | |
187 | printf(" %s any", key); | |
188 | } | |
189 | ||
190 | static void | |
191 | print_reject_code(int code) | |
192 | { | |
193 | struct icmpcode *ic; | |
194 | ||
195 | for (ic = icmpcodes; ic->str; ic++) | |
196 | if (ic->code == code) { | |
197 | printf("%s", ic->str); | |
198 | return; | |
199 | } | |
200 | printf("%u", code); | |
201 | } | |
202 | ||
203 | static void | |
204 | show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth) | |
205 | { | |
206 | char *comma; | |
207 | u_long adrt; | |
208 | struct hostent *he; | |
209 | struct protoent *pe; | |
210 | int i, mb; | |
211 | int nsp = IP_FW_GETNSRCP(chain); | |
212 | int ndp = IP_FW_GETNDSTP(chain); | |
213 | ||
214 | if (do_resolv) | |
7ba0088d | 215 | setservent(1/*stay open*/); |
b7080c8e A |
216 | |
217 | printf("%05u ", chain->fw_number); | |
218 | ||
7ba0088d A |
219 | if (do_acct) |
220 | printf("%*qu %*qu ", pcwidth, chain->fw_pcnt, bcwidth, chain->fw_bcnt); | |
b7080c8e | 221 | |
7ba0088d A |
222 | if (do_time) { |
223 | if (chain->timestamp) { | |
b7080c8e A |
224 | char timestr[30]; |
225 | ||
226 | strcpy(timestr, ctime((time_t *)&chain->timestamp)); | |
227 | *strchr(timestr, '\n') = '\0'; | |
228 | printf("%s ", timestr); | |
7ba0088d A |
229 | } else { |
230 | printf(" "); | |
b7080c8e | 231 | } |
7ba0088d A |
232 | } |
233 | if (chain->fw_flg == IP_FW_F_CHECK_S) { | |
234 | printf("check-state\n"); | |
235 | goto done; | |
b7080c8e A |
236 | } |
237 | ||
7ba0088d A |
238 | if (chain->fw_flg & IP_FW_F_RND_MATCH) { |
239 | double d = 1.0 * (int)(chain->pipe_ptr); | |
240 | d = 1 - (d / 0x7fffffff); | |
241 | printf("prob %f ", d); | |
242 | } | |
243 | ||
244 | switch (chain->fw_flg & IP_FW_F_COMMAND) { | |
b7080c8e A |
245 | case IP_FW_F_ACCEPT: |
246 | printf("allow"); | |
247 | break; | |
248 | case IP_FW_F_DENY: | |
249 | printf("deny"); | |
250 | break; | |
251 | case IP_FW_F_COUNT: | |
252 | printf("count"); | |
253 | break; | |
254 | case IP_FW_F_DIVERT: | |
255 | printf("divert %u", chain->fw_divert_port); | |
256 | break; | |
257 | case IP_FW_F_TEE: | |
258 | printf("tee %u", chain->fw_divert_port); | |
259 | break; | |
260 | case IP_FW_F_SKIPTO: | |
261 | printf("skipto %u", chain->fw_skipto_rule); | |
262 | break; | |
7ba0088d A |
263 | |
264 | case IP_FW_F_PIPE: | |
265 | printf("pipe %u", chain->fw_skipto_rule); | |
266 | break; | |
267 | case IP_FW_F_QUEUE: | |
268 | printf("queue %u", chain->fw_skipto_rule); | |
269 | break; | |
b7080c8e A |
270 | case IP_FW_F_REJECT: |
271 | if (chain->fw_reject_code == IP_FW_REJECT_RST) | |
272 | printf("reset"); | |
273 | else { | |
274 | printf("unreach "); | |
275 | print_reject_code(chain->fw_reject_code); | |
276 | } | |
277 | break; | |
278 | case IP_FW_F_FWD: | |
279 | printf("fwd %s", inet_ntoa(chain->fw_fwd_ip.sin_addr)); | |
280 | if(chain->fw_fwd_ip.sin_port) | |
281 | printf(",%d", chain->fw_fwd_ip.sin_port); | |
282 | break; | |
283 | default: | |
284 | errx(EX_OSERR, "impossible"); | |
285 | } | |
7ba0088d A |
286 | |
287 | if (chain->fw_flg & IP_FW_F_PRN) { | |
b7080c8e | 288 | printf(" log"); |
7ba0088d A |
289 | if (chain->fw_logamount) |
290 | printf(" logamount %d", chain->fw_logamount); | |
291 | } | |
b7080c8e A |
292 | |
293 | pe = getprotobynumber(chain->fw_prot); | |
294 | if (pe) | |
295 | printf(" %s", pe->p_name); | |
296 | else | |
297 | printf(" %u", chain->fw_prot); | |
298 | ||
7ba0088d A |
299 | if (chain->fw_flg & IP_FW_F_SME) { |
300 | printf(" from me"); | |
b7080c8e | 301 | } else { |
7ba0088d A |
302 | printf(" from %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : ""); |
303 | ||
304 | adrt = ntohl(chain->fw_smsk.s_addr); | |
305 | if (adrt == ULONG_MAX && do_resolv) { | |
306 | adrt = (chain->fw_src.s_addr); | |
307 | he = gethostbyaddr((char *)&adrt, | |
308 | sizeof(u_long), AF_INET); | |
309 | if (he == NULL) { | |
310 | printf("%s", inet_ntoa(chain->fw_src)); | |
311 | } else | |
312 | printf("%s", he->h_name); | |
313 | } else { | |
314 | if (adrt != ULONG_MAX) { | |
315 | mb = mask_bits(chain->fw_smsk); | |
316 | if (mb == 0) { | |
317 | printf("any"); | |
b7080c8e | 318 | } else { |
7ba0088d A |
319 | if (mb > 0) { |
320 | printf("%s", inet_ntoa(chain->fw_src)); | |
321 | printf("/%d", mb); | |
322 | } else { | |
323 | printf("%s", inet_ntoa(chain->fw_src)); | |
324 | printf(":"); | |
325 | printf("%s", inet_ntoa(chain->fw_smsk)); | |
326 | } | |
b7080c8e | 327 | } |
7ba0088d A |
328 | } else |
329 | printf("%s", inet_ntoa(chain->fw_src)); | |
330 | } | |
b7080c8e A |
331 | } |
332 | ||
333 | if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) { | |
334 | comma = " "; | |
335 | for (i = 0; i < nsp; i++) { | |
336 | print_port(chain->fw_prot, chain->fw_uar.fw_pts[i], comma); | |
7ba0088d | 337 | if (i == 0 && (chain->fw_flg & IP_FW_F_SRNG)) |
b7080c8e | 338 | comma = "-"; |
7ba0088d A |
339 | else if (i == 0 && (chain->fw_flg & IP_FW_F_SMSK)) |
340 | comma = ":"; | |
b7080c8e A |
341 | else |
342 | comma = ","; | |
343 | } | |
344 | } | |
345 | ||
7ba0088d A |
346 | if (chain->fw_flg & IP_FW_F_DME) { |
347 | printf(" to me"); | |
b7080c8e | 348 | } else { |
7ba0088d A |
349 | printf(" to %s", chain->fw_flg & IP_FW_F_INVDST ? "not " : ""); |
350 | ||
351 | adrt = ntohl(chain->fw_dmsk.s_addr); | |
352 | if (adrt == ULONG_MAX && do_resolv) { | |
353 | adrt = (chain->fw_dst.s_addr); | |
354 | he = gethostbyaddr((char *)&adrt, | |
355 | sizeof(u_long), AF_INET); | |
356 | if (he == NULL) { | |
357 | printf("%s", inet_ntoa(chain->fw_dst)); | |
358 | } else | |
359 | printf("%s", he->h_name); | |
360 | } else { | |
361 | if (adrt != ULONG_MAX) { | |
362 | mb = mask_bits(chain->fw_dmsk); | |
363 | if (mb == 0) { | |
364 | printf("any"); | |
b7080c8e | 365 | } else { |
7ba0088d A |
366 | if (mb > 0) { |
367 | printf("%s", inet_ntoa(chain->fw_dst)); | |
368 | printf("/%d", mb); | |
369 | } else { | |
370 | printf("%s", inet_ntoa(chain->fw_dst)); | |
371 | printf(":"); | |
372 | printf("%s", inet_ntoa(chain->fw_dmsk)); | |
373 | } | |
b7080c8e | 374 | } |
7ba0088d A |
375 | } else |
376 | printf("%s", inet_ntoa(chain->fw_dst)); | |
377 | } | |
b7080c8e A |
378 | } |
379 | ||
380 | if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) { | |
381 | comma = " "; | |
382 | for (i = 0; i < ndp; i++) { | |
383 | print_port(chain->fw_prot, chain->fw_uar.fw_pts[nsp+i], comma); | |
7ba0088d | 384 | if (i == 0 && (chain->fw_flg & IP_FW_F_DRNG)) |
b7080c8e | 385 | comma = "-"; |
7ba0088d A |
386 | else if (i == 0 && (chain->fw_flg & IP_FW_F_DMSK)) |
387 | comma = ":"; | |
b7080c8e A |
388 | else |
389 | comma = ","; | |
390 | } | |
391 | } | |
392 | ||
7ba0088d A |
393 | if (chain->fw_flg & IP_FW_F_UID) { |
394 | struct passwd *pwd = getpwuid(chain->fw_uid); | |
395 | ||
396 | if (pwd) | |
397 | printf(" uid %s", pwd->pw_name); | |
398 | else | |
399 | printf(" uid %u", chain->fw_uid); | |
400 | } | |
401 | ||
402 | if (chain->fw_flg & IP_FW_F_KEEP_S) { | |
403 | if (chain->next_rule_ptr) | |
404 | printf(" keep-state %d", (int)chain->next_rule_ptr); | |
405 | else | |
406 | printf(" keep-state"); | |
407 | } | |
b7080c8e | 408 | /* Direction */ |
7ba0088d A |
409 | if (chain->fw_flg & IP_FW_BRIDGED) |
410 | printf(" bridged"); | |
b7080c8e A |
411 | if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT)) |
412 | printf(" in"); | |
413 | if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT)) | |
414 | printf(" out"); | |
415 | ||
416 | /* Handle hack for "via" backwards compatibility */ | |
417 | if ((chain->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { | |
418 | print_iface("via", | |
419 | &chain->fw_in_if, chain->fw_flg & IP_FW_F_IIFNAME); | |
420 | } else { | |
421 | /* Receive interface specified */ | |
422 | if (chain->fw_flg & IP_FW_F_IIFACE) | |
423 | print_iface("recv", &chain->fw_in_if, | |
424 | chain->fw_flg & IP_FW_F_IIFNAME); | |
425 | /* Transmit interface specified */ | |
426 | if (chain->fw_flg & IP_FW_F_OIFACE) | |
427 | print_iface("xmit", &chain->fw_out_if, | |
428 | chain->fw_flg & IP_FW_F_OIFNAME); | |
429 | } | |
430 | ||
431 | if (chain->fw_flg & IP_FW_F_FRAG) | |
432 | printf(" frag"); | |
433 | ||
434 | if (chain->fw_ipopt || chain->fw_ipnopt) { | |
435 | int _opt_printed = 0; | |
436 | #define PRINTOPT(x) {if (_opt_printed) printf(",");\ | |
437 | printf(x); _opt_printed = 1;} | |
438 | ||
439 | printf(" ipopt "); | |
440 | if (chain->fw_ipopt & IP_FW_IPOPT_SSRR) PRINTOPT("ssrr"); | |
441 | if (chain->fw_ipnopt & IP_FW_IPOPT_SSRR) PRINTOPT("!ssrr"); | |
442 | if (chain->fw_ipopt & IP_FW_IPOPT_LSRR) PRINTOPT("lsrr"); | |
443 | if (chain->fw_ipnopt & IP_FW_IPOPT_LSRR) PRINTOPT("!lsrr"); | |
444 | if (chain->fw_ipopt & IP_FW_IPOPT_RR) PRINTOPT("rr"); | |
445 | if (chain->fw_ipnopt & IP_FW_IPOPT_RR) PRINTOPT("!rr"); | |
446 | if (chain->fw_ipopt & IP_FW_IPOPT_TS) PRINTOPT("ts"); | |
447 | if (chain->fw_ipnopt & IP_FW_IPOPT_TS) PRINTOPT("!ts"); | |
7ba0088d | 448 | } |
b7080c8e | 449 | |
7ba0088d | 450 | if (chain->fw_ipflg & IP_FW_IF_TCPEST) |
b7080c8e A |
451 | printf(" established"); |
452 | else if (chain->fw_tcpf == IP_FW_TCPF_SYN && | |
453 | chain->fw_tcpnf == IP_FW_TCPF_ACK) | |
454 | printf(" setup"); | |
455 | else if (chain->fw_tcpf || chain->fw_tcpnf) { | |
456 | int _flg_printed = 0; | |
457 | #define PRINTFLG(x) {if (_flg_printed) printf(",");\ | |
458 | printf(x); _flg_printed = 1;} | |
459 | ||
7ba0088d | 460 | printf(" tcpflags "); |
b7080c8e A |
461 | if (chain->fw_tcpf & IP_FW_TCPF_FIN) PRINTFLG("fin"); |
462 | if (chain->fw_tcpnf & IP_FW_TCPF_FIN) PRINTFLG("!fin"); | |
463 | if (chain->fw_tcpf & IP_FW_TCPF_SYN) PRINTFLG("syn"); | |
464 | if (chain->fw_tcpnf & IP_FW_TCPF_SYN) PRINTFLG("!syn"); | |
465 | if (chain->fw_tcpf & IP_FW_TCPF_RST) PRINTFLG("rst"); | |
466 | if (chain->fw_tcpnf & IP_FW_TCPF_RST) PRINTFLG("!rst"); | |
467 | if (chain->fw_tcpf & IP_FW_TCPF_PSH) PRINTFLG("psh"); | |
468 | if (chain->fw_tcpnf & IP_FW_TCPF_PSH) PRINTFLG("!psh"); | |
469 | if (chain->fw_tcpf & IP_FW_TCPF_ACK) PRINTFLG("ack"); | |
470 | if (chain->fw_tcpnf & IP_FW_TCPF_ACK) PRINTFLG("!ack"); | |
471 | if (chain->fw_tcpf & IP_FW_TCPF_URG) PRINTFLG("urg"); | |
472 | if (chain->fw_tcpnf & IP_FW_TCPF_URG) PRINTFLG("!urg"); | |
7ba0088d A |
473 | } |
474 | if (chain->fw_tcpopt || chain->fw_tcpnopt) { | |
475 | int _opt_printed = 0; | |
476 | #define PRINTTOPT(x) {if (_opt_printed) printf(",");\ | |
477 | printf(x); _opt_printed = 1;} | |
478 | ||
479 | printf(" tcpoptions "); | |
480 | if (chain->fw_tcpopt & IP_FW_TCPOPT_MSS) PRINTTOPT("mss"); | |
481 | if (chain->fw_tcpnopt & IP_FW_TCPOPT_MSS) PRINTTOPT("!mss"); | |
482 | if (chain->fw_tcpopt & IP_FW_TCPOPT_WINDOW) PRINTTOPT("window"); | |
483 | if (chain->fw_tcpnopt & IP_FW_TCPOPT_WINDOW) PRINTTOPT("!window"); | |
484 | if (chain->fw_tcpopt & IP_FW_TCPOPT_SACK) PRINTTOPT("sack"); | |
485 | if (chain->fw_tcpnopt & IP_FW_TCPOPT_SACK) PRINTTOPT("!sack"); | |
486 | if (chain->fw_tcpopt & IP_FW_TCPOPT_TS) PRINTTOPT("ts"); | |
487 | if (chain->fw_tcpnopt & IP_FW_TCPOPT_TS) PRINTTOPT("!ts"); | |
488 | if (chain->fw_tcpopt & IP_FW_TCPOPT_CC) PRINTTOPT("cc"); | |
489 | if (chain->fw_tcpnopt & IP_FW_TCPOPT_CC) PRINTTOPT("!cc"); | |
490 | } | |
491 | ||
b7080c8e A |
492 | if (chain->fw_flg & IP_FW_F_ICMPBIT) { |
493 | int type_index; | |
494 | int first = 1; | |
495 | ||
496 | printf(" icmptype"); | |
497 | ||
498 | for (type_index = 0; type_index < IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index) | |
7ba0088d | 499 | if (chain->fw_uar.fw_icmptypes[type_index / (sizeof(unsigned) * 8)] & |
b7080c8e A |
500 | (1U << (type_index % (sizeof(unsigned) * 8)))) { |
501 | printf("%c%d", first == 1 ? ' ' : ',', type_index); | |
502 | first = 0; | |
503 | } | |
504 | } | |
505 | printf("\n"); | |
7ba0088d | 506 | done: |
b7080c8e A |
507 | if (do_resolv) |
508 | endservent(); | |
509 | } | |
510 | ||
7ba0088d A |
511 | #ifdef DUMMYNET |
512 | int | |
513 | sort_q(const void *pa, const void *pb) | |
514 | { | |
515 | int rev = (do_sort < 0); | |
516 | int field = rev ? -do_sort : do_sort; | |
517 | long long res = 0; | |
518 | const struct dn_flow_queue *a = pa; | |
519 | const struct dn_flow_queue *b = pb; | |
520 | ||
521 | switch (field) { | |
522 | case 1: /* pkts */ | |
523 | res = a->len - b->len; | |
524 | break; | |
525 | case 2 : /* bytes */ | |
526 | res = a->len_bytes - b->len_bytes; | |
527 | break; | |
528 | ||
529 | case 3 : /* tot pkts */ | |
530 | res = a->tot_pkts - b->tot_pkts; | |
531 | break; | |
532 | ||
533 | case 4 : /* tot bytes */ | |
534 | res = a->tot_bytes - b->tot_bytes; | |
535 | break; | |
536 | } | |
537 | if (res < 0) | |
538 | res = -1; | |
539 | if (res > 0) | |
540 | res = 1; | |
541 | return (int)(rev ? res : -res); | |
542 | } | |
543 | ||
544 | static void | |
545 | list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) | |
546 | { | |
547 | int l; | |
548 | ||
549 | printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", | |
550 | fs->flow_mask.proto, | |
551 | fs->flow_mask.src_ip, fs->flow_mask.src_port, | |
552 | fs->flow_mask.dst_ip, fs->flow_mask.dst_port); | |
553 | if (fs->rq_elements == 0) | |
554 | return; | |
555 | ||
556 | printf("BKT Prot ___Source IP/port____ " | |
557 | "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); | |
558 | if (do_sort != 0) | |
559 | heapsort(q, fs->rq_elements, sizeof(*q), sort_q); | |
560 | for (l = 0; l < fs->rq_elements; l++) { | |
561 | struct in_addr ina; | |
562 | struct protoent *pe; | |
563 | ||
564 | ina.s_addr = htonl(q[l].id.src_ip); | |
565 | printf("%3d ", q[l].hash_slot); | |
566 | pe = getprotobynumber(q[l].id.proto); | |
567 | if (pe) | |
568 | printf("%-4s ", pe->p_name); | |
569 | else | |
570 | printf("%4u ", q[l].id.proto); | |
571 | printf("%15s/%-5d ", inet_ntoa(ina), q[l].id.src_port); | |
572 | ina.s_addr = htonl(q[l].id.dst_ip); | |
573 | printf("%15s/%-5d ", | |
574 | inet_ntoa(ina), q[l].id.dst_port); | |
575 | printf("%4qu %8qu %2u %4u %3u\n", | |
576 | q[l].tot_pkts, q[l].tot_bytes, | |
577 | q[l].len, q[l].len_bytes, q[l].drops); | |
578 | if (verbose) | |
579 | printf(" S %20qd F %20qd\n", q[l].S, q[l].F); | |
580 | } | |
581 | } | |
582 | ||
583 | static void | |
584 | print_flowset_parms(struct dn_flow_set *fs, char *prefix) | |
585 | { | |
586 | int l; | |
587 | char qs[30]; | |
588 | char plr[30]; | |
589 | char red[90]; /* Display RED parameters */ | |
590 | ||
591 | l = fs->qsize; | |
592 | if (fs->flags_fs & DN_QSIZE_IS_BYTES) { | |
593 | if (l >= 8192) | |
594 | sprintf(qs, "%d KB", l / 1024); | |
595 | else | |
596 | sprintf(qs, "%d B", l); | |
597 | } else | |
598 | sprintf(qs, "%3d sl.", l); | |
599 | if (fs->plr) | |
600 | sprintf(plr, "plr %f", 1.0*fs->plr/(double)(0x7fffffff)); | |
601 | else | |
602 | plr[0]='\0'; | |
603 | if (fs->flags_fs & DN_IS_RED) /* RED parameters */ | |
604 | sprintf(red, | |
605 | "\n %cRED w_q %f min_th %d max_th %d max_p %f", | |
606 | (fs->flags_fs & DN_IS_GENTLE_RED)? 'G' : ' ', | |
607 | 1.0 * fs->w_q / (double)(1 << SCALE_RED), | |
608 | SCALE_VAL(fs->min_th), | |
609 | SCALE_VAL(fs->max_th), | |
610 | 1.0 * fs->max_p / (double)(1 << SCALE_RED) ) ; | |
611 | else | |
612 | sprintf(red, "droptail"); | |
613 | ||
614 | printf("%s %s%s %d queues (%d buckets) %s\n", prefix, qs, plr, | |
615 | fs->rq_elements, fs->rq_size, red); | |
616 | } | |
617 | #endif /* DUMMYNET */ | |
618 | ||
b7080c8e A |
619 | static void |
620 | list(ac, av) | |
621 | int ac; | |
622 | char **av; | |
623 | { | |
624 | struct ip_fw *rules; | |
7ba0088d | 625 | #ifdef DUMMYNET |
b7080c8e | 626 | struct dn_pipe *pipes; |
7ba0088d | 627 | #endif /* DUMMYNET */ |
b7080c8e A |
628 | void *data = NULL; |
629 | int pcwidth = 0; | |
630 | int bcwidth = 0; | |
631 | int n, num = 0; | |
7ba0088d | 632 | int nbytes; |
b7080c8e A |
633 | |
634 | /* get rules or pipes from kernel, resizing array as necessary */ | |
635 | { | |
7ba0088d | 636 | #ifdef DUMMYNET |
b7080c8e A |
637 | const int unit = do_pipe ? sizeof(*pipes) : sizeof(*rules); |
638 | const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET; | |
7ba0088d A |
639 | #else /* DUMMYNET */ |
640 | const int unit = sizeof(*rules); | |
641 | const int ocmd = IP_FW_GET; | |
642 | #endif /* DUMMYNET */ | |
643 | int nalloc = unit; | |
644 | nbytes = nalloc; | |
645 | ||
646 | while (nbytes >= nalloc) { | |
b7080c8e | 647 | nalloc = nalloc * 2 + 200; |
7ba0088d | 648 | nbytes = nalloc; |
b7080c8e A |
649 | if ((data = realloc(data, nbytes)) == NULL) |
650 | err(EX_OSERR, "realloc"); | |
7ba0088d A |
651 | rules = data; |
652 | rules->version = IP_FW_CURRENT_API_VERSION; | |
b7080c8e | 653 | if (getsockopt(s, IPPROTO_IP, ocmd, data, &nbytes) < 0) |
7ba0088d | 654 | #ifdef DUMMYNET |
b7080c8e A |
655 | err(EX_OSERR, "getsockopt(IP_%s_GET)", |
656 | do_pipe ? "DUMMYNET" : "FW"); | |
7ba0088d A |
657 | #else /* DUMMYNET */ |
658 | err(EX_OSERR, "getsockopt(IP_FW_GET)"); | |
659 | #endif /* DUMMYNET */ | |
b7080c8e A |
660 | } |
661 | } | |
662 | ||
663 | /* display requested pipes */ | |
7ba0088d | 664 | #ifdef DUMMYNET |
b7080c8e | 665 | if (do_pipe) { |
7ba0088d A |
666 | u_long rulenum; |
667 | void *next = data; | |
668 | struct dn_pipe *p = (struct dn_pipe *) data; | |
669 | struct dn_flow_set *fs; | |
670 | struct dn_flow_queue *q; | |
671 | int l; | |
672 | ||
673 | if (ac > 0) | |
674 | rulenum = strtoul(*av++, NULL, 10); | |
b7080c8e | 675 | else |
7ba0088d A |
676 | rulenum = 0; |
677 | for (; nbytes >= sizeof(*p); p = (struct dn_pipe *)next) { | |
678 | double b = p->bandwidth; | |
679 | char buf[30]; | |
680 | char prefix[80]; | |
b7080c8e | 681 | |
7ba0088d A |
682 | if (p->next != (struct dn_pipe *)DN_IS_PIPE) |
683 | break; | |
684 | l = sizeof(*p) + p->fs.rq_elements * sizeof(*q); | |
685 | next = (void *)p + l; | |
686 | nbytes -= l; | |
687 | q = (struct dn_flow_queue *)(p+1); | |
688 | ||
689 | if (rulenum != 0 && rulenum != p->pipe_nr) | |
690 | continue; | |
691 | if (p->if_name[0] != '\0') | |
692 | sprintf(buf, "%s", p->if_name); | |
693 | else if (b == 0) | |
694 | sprintf(buf, "unlimited"); | |
695 | else if (b >= 1000000) | |
696 | sprintf(buf, "%7.3f Mbit/s", b/1000000); | |
697 | else if (b >= 1000) | |
698 | sprintf(buf, "%7.3f Kbit/s", b/1000); | |
699 | else | |
700 | sprintf(buf, "%7.3f bit/s ", b); | |
701 | ||
702 | sprintf(prefix, "%05d: %s %4d ms ", | |
703 | p->pipe_nr, buf, p->delay); | |
704 | print_flowset_parms(&(p->fs), prefix); | |
705 | if (verbose) | |
706 | printf(" V %20qd\n", p->V >> MY_M); | |
707 | list_queues(&(p->fs), q); | |
708 | } | |
709 | fs = (struct dn_flow_set *) next; | |
710 | for (; nbytes >= sizeof(*fs); fs = (struct dn_flow_set *)next) { | |
711 | char prefix[80]; | |
712 | ||
713 | if (fs->next != (struct dn_flow_set *)DN_IS_QUEUE) | |
714 | break; | |
715 | l = sizeof(*fs) + fs->rq_elements * sizeof(*q); | |
716 | next = (void *)fs + l; | |
717 | nbytes -= l; | |
718 | q = (struct dn_flow_queue *)(fs+1); | |
719 | sprintf(prefix, "q%05d: weight %d pipe %d ", | |
720 | fs->fs_nr, fs->weight, fs->parent_nr); | |
721 | print_flowset_parms(fs, prefix); | |
722 | list_queues(fs, q); | |
723 | } | |
724 | free(data); | |
725 | return; | |
b7080c8e | 726 | } |
7ba0088d | 727 | #endif /* DUMMYNET */ |
b7080c8e | 728 | |
b7080c8e | 729 | rules = (struct ip_fw *) data; |
7ba0088d A |
730 | /* determine num more accurately */ |
731 | num = 0; | |
732 | while (rules[num].fw_number < 65535) | |
733 | num++; | |
734 | num++; /* counting starts from 0 ... */ | |
735 | /* if showing stats, figure out column widths ahead of time */ | |
b7080c8e A |
736 | if (do_acct) { |
737 | for (n = 0; n < num; n++) { | |
738 | struct ip_fw *const r = &rules[n]; | |
739 | char temp[32]; | |
740 | int width; | |
741 | ||
742 | /* packet counter */ | |
743 | width = sprintf(temp, "%qu", r->fw_pcnt); | |
744 | if (width > pcwidth) | |
745 | pcwidth = width; | |
746 | ||
747 | /* byte counter */ | |
748 | width = sprintf(temp, "%qu", r->fw_bcnt); | |
749 | if (width > bcwidth) | |
750 | bcwidth = width; | |
751 | } | |
752 | } | |
753 | if (ac == 0) { | |
754 | /* display all rules */ | |
755 | for (n = 0; n < num; n++) { | |
756 | struct ip_fw *const r = &rules[n]; | |
757 | ||
758 | show_ipfw(r, pcwidth, bcwidth); | |
759 | } | |
760 | } else { | |
761 | /* display specific rules requested on command line */ | |
762 | int exitval = EX_OK; | |
763 | ||
764 | while (ac--) { | |
765 | u_long rnum; | |
766 | char *endptr; | |
767 | int seen; | |
768 | ||
769 | /* convert command line rule # */ | |
770 | rnum = strtoul(*av++, &endptr, 10); | |
771 | if (*endptr) { | |
772 | exitval = EX_USAGE; | |
773 | warnx("invalid rule number: %s", *(av - 1)); | |
774 | continue; | |
775 | } | |
776 | for (seen = n = 0; n < num; n++) { | |
777 | struct ip_fw *const r = &rules[n]; | |
778 | ||
779 | if (r->fw_number > rnum) | |
780 | break; | |
781 | if (r->fw_number == rnum) { | |
782 | show_ipfw(r, pcwidth, bcwidth); | |
783 | seen = 1; | |
784 | } | |
785 | } | |
786 | if (!seen) { | |
787 | /* give precedence to other error(s) */ | |
788 | if (exitval == EX_OK) | |
789 | exitval = EX_UNAVAILABLE; | |
790 | warnx("rule %lu does not exist", rnum); | |
791 | } | |
792 | } | |
793 | if (exitval != EX_OK) | |
794 | exit(exitval); | |
795 | } | |
7ba0088d A |
796 | /* |
797 | * show dynamic rules | |
798 | */ | |
799 | if (num * sizeof (rules[0]) != nbytes) { | |
800 | struct ipfw_dyn_rule *d = | |
801 | (struct ipfw_dyn_rule *)&rules[num]; | |
802 | struct in_addr a; | |
803 | struct protoent *pe; | |
804 | ||
805 | printf("## Dynamic rules:\n"); | |
806 | for (;; d++) { | |
807 | printf("%05d %qu %qu (T %d, # %d) ty %d", | |
808 | (int)(d->chain), | |
809 | d->pcnt, d->bcnt, | |
810 | d->expire, | |
811 | d->bucket, | |
812 | d->type); | |
813 | pe = getprotobynumber(d->id.proto); | |
814 | if (pe) | |
815 | printf(" %s,", pe->p_name); | |
816 | else | |
817 | printf(" %u,", d->id.proto); | |
818 | a.s_addr = htonl(d->id.src_ip); | |
819 | printf(" %s", inet_ntoa(a)); | |
820 | printf(" %d", d->id.src_port); | |
821 | switch (d->type) { | |
822 | default: /* bidir, no mask */ | |
823 | printf(" <->"); | |
824 | break; | |
825 | } | |
826 | a.s_addr = htonl(d->id.dst_ip); | |
827 | printf(" %s", inet_ntoa(a)); | |
828 | printf(" %d", d->id.dst_port); | |
829 | printf("\n"); | |
830 | if (d->next == NULL) | |
831 | break; | |
832 | } | |
833 | } | |
834 | ||
b7080c8e A |
835 | free(data); |
836 | } | |
837 | ||
838 | static void | |
839 | show_usage(const char *fmt, ...) | |
840 | { | |
841 | if (fmt) { | |
842 | char buf[100]; | |
843 | va_list args; | |
844 | ||
845 | va_start(args, fmt); | |
846 | vsnprintf(buf, sizeof(buf), fmt, args); | |
847 | va_end(args); | |
848 | warnx("error: %s", buf); | |
849 | } | |
850 | fprintf(stderr, "usage: ipfw [options]\n" | |
7ba0088d A |
851 | #ifdef DUMMYNET |
852 | " [pipe] flush\n" | |
853 | #endif /* DUMMYNET */ | |
b7080c8e | 854 | " add [number] rule\n" |
7ba0088d A |
855 | #ifdef DUMMYNET |
856 | " [pipe] delete number ...\n" | |
857 | " [pipe] list [number ...]\n" | |
858 | " [pipe] show [number ...]\n" | |
859 | #endif /* DUMMYNET */ | |
b7080c8e | 860 | " zero [number ...]\n" |
7ba0088d A |
861 | " resetlog [number ...]\n" |
862 | #ifdef DUMMYNET | |
863 | " pipe number config [pipeconfig]\n" | |
864 | #endif /* DUMMYNET */ | |
865 | " rule: [prob <match_probability>] action proto src dst extras...\n" | |
b7080c8e A |
866 | " action:\n" |
867 | " {allow|permit|accept|pass|deny|drop|reject|unreach code|\n" | |
7ba0088d A |
868 | " reset|count|skipto num|divert port|tee port|fwd ip|\n" |
869 | #ifdef DUMMYNET | |
870 | " pipe num" | |
871 | #endif /* DUMMYNET */ | |
872 | "} [log [logamount count]]\n" | |
b7080c8e | 873 | " proto: {ip|tcp|udp|icmp|<number>}\n" |
7ba0088d A |
874 | " src: from [not] {me|any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" |
875 | " dst: to [not] {me|any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" | |
b7080c8e | 876 | " extras:\n" |
7ba0088d | 877 | " uid {user id}\n" |
b7080c8e A |
878 | " fragment (may not be used with ports or tcpflags)\n" |
879 | " in\n" | |
880 | " out\n" | |
881 | " {xmit|recv|via} {iface|ip|any}\n" | |
882 | " {established|setup}\n" | |
883 | " tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n" | |
884 | " ipoptions [!]{ssrr|lsrr|rr|ts},...\n" | |
7ba0088d A |
885 | " tcpoptions [!]{mss|window|sack|ts|cc},...\n" |
886 | " icmptypes {type[,type]}...\n" | |
887 | #ifdef DUMMYNET | |
888 | " pipeconfig:\n" | |
889 | " {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n" | |
890 | " {bw|bandwidth} interface_name\n" | |
891 | " delay <milliseconds>\n" | |
892 | " queue <size>{packets|Bytes|KBytes}\n" | |
893 | " plr <fraction>\n" | |
894 | " mask {all| [dst-ip|src-ip|dst-port|src-port|proto] <number>}\n" | |
895 | " buckets <number>}\n" | |
896 | " {red|gred} <fraction>/<number>/<number>/<fraction>\n" | |
897 | " droptail\n" | |
898 | #endif /* DUMMYNET */ | |
899 | ); | |
b7080c8e A |
900 | |
901 | exit(EX_USAGE); | |
902 | } | |
903 | ||
904 | static int | |
905 | lookup_host (host, ipaddr) | |
906 | char *host; | |
907 | struct in_addr *ipaddr; | |
908 | { | |
7ba0088d | 909 | struct hostent *he; |
b7080c8e | 910 | |
7ba0088d A |
911 | if (!inet_aton(host, ipaddr)) { |
912 | if ((he = gethostbyname(host)) == NULL) | |
913 | return(-1); | |
914 | *ipaddr = *(struct in_addr *)he->h_addr_list[0]; | |
915 | } | |
b7080c8e A |
916 | return(0); |
917 | } | |
918 | ||
919 | static void | |
920 | fill_ip(ipno, mask, acp, avp) | |
921 | struct in_addr *ipno, *mask; | |
922 | int *acp; | |
923 | char ***avp; | |
924 | { | |
925 | int ac = *acp; | |
926 | char **av = *avp; | |
927 | char *p = 0, md = 0; | |
928 | ||
7ba0088d | 929 | if (ac && !strncmp(*av, "any", strlen(*av))) { |
b7080c8e A |
930 | ipno->s_addr = mask->s_addr = 0; av++; ac--; |
931 | } else { | |
932 | p = strchr(*av, '/'); | |
7ba0088d | 933 | if (!p) |
b7080c8e A |
934 | p = strchr(*av, ':'); |
935 | if (p) { | |
936 | md = *p; | |
7ba0088d | 937 | *p++ = '\0'; |
b7080c8e A |
938 | } |
939 | ||
940 | if (lookup_host(*av, ipno) != 0) | |
941 | show_usage("hostname ``%s'' unknown", *av); | |
942 | switch (md) { | |
943 | case ':': | |
7ba0088d | 944 | if (!inet_aton(p, mask)) |
b7080c8e A |
945 | show_usage("bad netmask ``%s''", p); |
946 | break; | |
947 | case '/': | |
948 | if (atoi(p) == 0) { | |
949 | mask->s_addr = 0; | |
950 | } else if (atoi(p) > 32) { | |
951 | show_usage("bad width ``%s''", p); | |
952 | } else { | |
953 | mask->s_addr = | |
954 | htonl(~0 << (32 - atoi(p))); | |
955 | } | |
956 | break; | |
957 | default: | |
958 | mask->s_addr = htonl(~0); | |
959 | break; | |
960 | } | |
961 | ipno->s_addr &= mask->s_addr; | |
962 | av++; | |
963 | ac--; | |
964 | } | |
965 | *acp = ac; | |
966 | *avp = av; | |
967 | } | |
968 | ||
969 | static void | |
970 | fill_reject_code(u_short *codep, char *str) | |
971 | { | |
972 | struct icmpcode *ic; | |
973 | u_long val; | |
974 | char *s; | |
975 | ||
976 | val = strtoul(str, &s, 0); | |
977 | if (s != str && *s == '\0' && val < 0x100) { | |
978 | *codep = val; | |
979 | return; | |
980 | } | |
981 | for (ic = icmpcodes; ic->str; ic++) | |
982 | if (!strcasecmp(str, ic->str)) { | |
983 | *codep = ic->code; | |
984 | return; | |
985 | } | |
986 | show_usage("unknown ICMP unreachable code ``%s''", str); | |
987 | } | |
988 | ||
989 | static void | |
990 | add_port(cnt, ptr, off, port) | |
991 | u_short *cnt, *ptr, off, port; | |
992 | { | |
993 | if (off + *cnt >= IP_FW_MAX_PORTS) | |
994 | errx(EX_USAGE, "too many ports (max is %d)", IP_FW_MAX_PORTS); | |
995 | ptr[off+*cnt] = port; | |
996 | (*cnt)++; | |
997 | } | |
998 | ||
999 | static int | |
7ba0088d | 1000 | lookup_port(const char *arg, int proto, int test, int nodash) |
b7080c8e A |
1001 | { |
1002 | int val; | |
1003 | char *earg, buf[32]; | |
1004 | struct servent *s; | |
7ba0088d | 1005 | char *p, *q; |
b7080c8e A |
1006 | |
1007 | snprintf(buf, sizeof(buf), "%s", arg); | |
7ba0088d A |
1008 | |
1009 | for (p = q = buf; *p; *q++ = *p++) { | |
1010 | if (*p == '\\') { | |
1011 | if (*(p+1)) | |
1012 | p++; | |
1013 | } else { | |
1014 | if (*p == ',' || (nodash && *p == '-')) | |
1015 | break; | |
1016 | } | |
1017 | } | |
1018 | *q = '\0'; | |
1019 | ||
b7080c8e A |
1020 | val = (int) strtoul(buf, &earg, 0); |
1021 | if (!*buf || *earg) { | |
7ba0088d A |
1022 | char *protocol = NULL; |
1023 | ||
1024 | if (proto != 0) { | |
1025 | struct protoent *pe = getprotobynumber(proto); | |
1026 | ||
1027 | if (pe) | |
1028 | protocol = pe->p_name; | |
1029 | } | |
1030 | ||
b7080c8e | 1031 | setservent(1); |
7ba0088d | 1032 | if ((s = getservbyname(buf, protocol))) { |
b7080c8e A |
1033 | val = htons(s->s_port); |
1034 | } else { | |
1035 | if (!test) { | |
7ba0088d | 1036 | errx(EX_DATAERR, "unknown port ``%s''", buf); |
b7080c8e A |
1037 | } |
1038 | val = -1; | |
1039 | } | |
1040 | } else { | |
1041 | if (val < 0 || val > 0xffff) { | |
1042 | if (!test) { | |
7ba0088d | 1043 | errx(EX_DATAERR, "port ``%s'' out of range", buf); |
b7080c8e A |
1044 | } |
1045 | val = -1; | |
1046 | } | |
1047 | } | |
1048 | return(val); | |
1049 | } | |
1050 | ||
7ba0088d A |
1051 | /* |
1052 | * return: 0 normally, 1 if first pair is a range, | |
1053 | * 2 if first pair is a port+mask | |
1054 | */ | |
b7080c8e | 1055 | static int |
7ba0088d | 1056 | fill_port(u_short *cnt, u_short *ptr, u_short off, char *arg, int proto) |
b7080c8e A |
1057 | { |
1058 | char *s; | |
1059 | int initial_range = 0; | |
1060 | ||
7ba0088d A |
1061 | for (s = arg; *s && *s != ',' && *s != '-' && *s != ':'; s++) { |
1062 | if (*s == '\\' && *(s+1)) | |
1063 | s++; | |
1064 | } | |
1065 | if (*s == ':') { | |
1066 | *s++ = '\0'; | |
1067 | if (strchr(arg, ',')) | |
1068 | errx(EX_USAGE, "port/mask must be first in list"); | |
1069 | add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0x0000); | |
1070 | arg = s; | |
1071 | s = strchr(arg,','); | |
1072 | if (s) | |
1073 | *s++ = '\0'; | |
1074 | add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0xffff); | |
1075 | arg = s; | |
1076 | initial_range = 2; | |
1077 | } else | |
b7080c8e A |
1078 | if (*s == '-') { |
1079 | *s++ = '\0'; | |
1080 | if (strchr(arg, ',')) | |
1081 | errx(EX_USAGE, "port range must be first in list"); | |
7ba0088d | 1082 | add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0x0000); |
b7080c8e A |
1083 | arg = s; |
1084 | s = strchr(arg,','); | |
1085 | if (s) | |
1086 | *s++ = '\0'; | |
7ba0088d | 1087 | add_port(cnt, ptr, off, *arg ? lookup_port(arg, proto, 0, 0) : 0xffff); |
b7080c8e A |
1088 | arg = s; |
1089 | initial_range = 1; | |
1090 | } | |
1091 | while (arg != NULL) { | |
1092 | s = strchr(arg,','); | |
1093 | if (s) | |
1094 | *s++ = '\0'; | |
7ba0088d | 1095 | add_port(cnt, ptr, off, lookup_port(arg, proto, 0, 0)); |
b7080c8e A |
1096 | arg = s; |
1097 | } | |
1098 | return initial_range; | |
1099 | } | |
1100 | ||
1101 | static void | |
7ba0088d | 1102 | fill_tcpflag(u_char *set, u_char *reset, char **vp) |
b7080c8e A |
1103 | { |
1104 | char *p = *vp,*q; | |
1105 | u_char *d; | |
1106 | ||
1107 | while (p && *p) { | |
1108 | struct tpcflags { | |
1109 | char * name; | |
1110 | u_char value; | |
1111 | } flags[] = { | |
1112 | { "syn", IP_FW_TCPF_SYN }, | |
1113 | { "fin", IP_FW_TCPF_FIN }, | |
1114 | { "ack", IP_FW_TCPF_ACK }, | |
1115 | { "psh", IP_FW_TCPF_PSH }, | |
1116 | { "rst", IP_FW_TCPF_RST }, | |
1117 | { "urg", IP_FW_TCPF_URG } | |
1118 | }; | |
1119 | int i; | |
1120 | ||
1121 | if (*p == '!') { | |
1122 | p++; | |
1123 | d = reset; | |
1124 | } else { | |
1125 | d = set; | |
1126 | } | |
1127 | q = strchr(p, ','); | |
7ba0088d | 1128 | if (q) |
b7080c8e A |
1129 | *q++ = '\0'; |
1130 | for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) | |
1131 | if (!strncmp(p, flags[i].name, strlen(p))) { | |
1132 | *d |= flags[i].value; | |
1133 | break; | |
1134 | } | |
1135 | if (i == sizeof(flags) / sizeof(flags[0])) | |
1136 | show_usage("invalid tcp flag ``%s''", p); | |
1137 | p = q; | |
1138 | } | |
1139 | } | |
1140 | ||
7ba0088d A |
1141 | static void |
1142 | fill_tcpopts(u_char *set, u_char *reset, char **vp) | |
1143 | { | |
1144 | char *p = *vp,*q; | |
1145 | u_char *d; | |
1146 | ||
1147 | while (p && *p) { | |
1148 | struct tpcopts { | |
1149 | char * name; | |
1150 | u_char value; | |
1151 | } opts[] = { | |
1152 | { "mss", IP_FW_TCPOPT_MSS }, | |
1153 | { "window", IP_FW_TCPOPT_WINDOW }, | |
1154 | { "sack", IP_FW_TCPOPT_SACK }, | |
1155 | { "ts", IP_FW_TCPOPT_TS }, | |
1156 | { "cc", IP_FW_TCPOPT_CC }, | |
1157 | }; | |
1158 | int i; | |
1159 | ||
1160 | if (*p == '!') { | |
1161 | p++; | |
1162 | d = reset; | |
1163 | } else { | |
1164 | d = set; | |
1165 | } | |
1166 | q = strchr(p, ','); | |
1167 | if (q) | |
1168 | *q++ = '\0'; | |
1169 | for (i = 0; i < sizeof(opts) / sizeof(opts[0]); ++i) | |
1170 | if (!strncmp(p, opts[i].name, strlen(p))) { | |
1171 | *d |= opts[i].value; | |
1172 | break; | |
1173 | } | |
1174 | if (i == sizeof(opts) / sizeof(opts[0])) | |
1175 | show_usage("invalid tcp option ``%s''", p); | |
1176 | p = q; | |
1177 | } | |
1178 | } | |
1179 | ||
b7080c8e A |
1180 | static void |
1181 | fill_ipopt(u_char *set, u_char *reset, char **vp) | |
1182 | { | |
1183 | char *p = *vp,*q; | |
1184 | u_char *d; | |
1185 | ||
1186 | while (p && *p) { | |
1187 | if (*p == '!') { | |
1188 | p++; | |
1189 | d = reset; | |
1190 | } else { | |
1191 | d = set; | |
1192 | } | |
1193 | q = strchr(p, ','); | |
7ba0088d | 1194 | if (q) |
b7080c8e | 1195 | *q++ = '\0'; |
7ba0088d A |
1196 | if (!strncmp(p, "ssrr", strlen(p))) *d |= IP_FW_IPOPT_SSRR; |
1197 | if (!strncmp(p, "lsrr", strlen(p))) *d |= IP_FW_IPOPT_LSRR; | |
1198 | if (!strncmp(p, "rr", strlen(p))) *d |= IP_FW_IPOPT_RR; | |
1199 | if (!strncmp(p, "ts", strlen(p))) *d |= IP_FW_IPOPT_TS; | |
b7080c8e A |
1200 | p = q; |
1201 | } | |
1202 | } | |
1203 | ||
1204 | static void | |
1205 | fill_icmptypes(types, vp, fw_flg) | |
1206 | u_long *types; | |
1207 | char **vp; | |
1208 | u_int *fw_flg; | |
1209 | { | |
1210 | char *c = *vp; | |
1211 | ||
1212 | while (*c) | |
1213 | { | |
1214 | unsigned long icmptype; | |
1215 | ||
7ba0088d | 1216 | if (*c == ',') |
b7080c8e A |
1217 | ++c; |
1218 | ||
1219 | icmptype = strtoul(c, &c, 0); | |
1220 | ||
7ba0088d | 1221 | if (*c != ',' && *c != '\0') |
b7080c8e A |
1222 | show_usage("invalid ICMP type"); |
1223 | ||
1224 | if (icmptype >= IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8) | |
1225 | show_usage("ICMP type out of range"); | |
1226 | ||
7ba0088d | 1227 | types[icmptype / (sizeof(unsigned) * 8)] |= |
b7080c8e A |
1228 | 1 << (icmptype % (sizeof(unsigned) * 8)); |
1229 | *fw_flg |= IP_FW_F_ICMPBIT; | |
1230 | } | |
1231 | } | |
1232 | ||
1233 | static void | |
7ba0088d | 1234 | delete(ac, av) |
b7080c8e A |
1235 | int ac; |
1236 | char **av; | |
1237 | { | |
1238 | struct ip_fw rule; | |
7ba0088d | 1239 | #ifdef DUMMYNET |
b7080c8e | 1240 | struct dn_pipe pipe; |
7ba0088d | 1241 | #endif /* DUMMYNET */ |
b7080c8e A |
1242 | int i; |
1243 | int exitval = EX_OK; | |
1244 | ||
1245 | memset(&rule, 0, sizeof rule); | |
7ba0088d | 1246 | #ifdef DUMMYNET |
b7080c8e | 1247 | memset(&pipe, 0, sizeof pipe); |
7ba0088d A |
1248 | #endif /* DUMMYNET */ |
1249 | rule.version = IP_FW_CURRENT_API_VERSION; | |
b7080c8e A |
1250 | |
1251 | av++; ac--; | |
1252 | ||
1253 | /* Rule number */ | |
7ba0088d A |
1254 | while (ac && isdigit(**av)) |
1255 | { | |
1256 | i = atoi(*av); av++; ac--; | |
1257 | ||
1258 | #ifdef DUMMYNET | |
1259 | if (do_pipe) | |
1260 | { | |
1261 | if (do_pipe == 1) pipe.pipe_nr = i; | |
1262 | else pipe.fs.fs_nr = i; | |
1263 | ||
1264 | i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL, &pipe, sizeof pipe); | |
1265 | if (i) | |
1266 | { | |
1267 | exitval = 1; | |
1268 | warn("rule %u: setsockopt(%s)", do_pipe == 1 ? pipe.pipe_nr : pipe.fs.fs_nr, | |
1269 | "IP_DUMMYNET_DEL"); | |
1270 | } | |
1271 | } | |
1272 | else | |
1273 | #endif /* DUMMYNET */ | |
1274 | { | |
1275 | rule.fw_number = i; | |
1276 | i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule); | |
1277 | if (i) | |
1278 | { | |
1279 | exitval = EX_UNAVAILABLE; | |
1280 | warn("rule %u: setsockopt(%s)", rule.fw_number, "IP_FW_DEL"); | |
1281 | } | |
b7080c8e A |
1282 | } |
1283 | } | |
7ba0088d | 1284 | |
b7080c8e A |
1285 | if (exitval != EX_OK) |
1286 | exit(exitval); | |
1287 | } | |
1288 | ||
1289 | static void | |
1290 | verify_interface(union ip_fw_if *ifu) | |
1291 | { | |
1292 | struct ifreq ifr; | |
1293 | ||
1294 | /* | |
1295 | * If a unit was specified, check for that exact interface. | |
1296 | * If a wildcard was specified, check for unit 0. | |
1297 | */ | |
7ba0088d | 1298 | snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", |
b7080c8e A |
1299 | ifu->fu_via_if.name, |
1300 | ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit); | |
1301 | ||
1302 | if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) | |
1303 | warnx("warning: interface ``%s'' does not exist", ifr.ifr_name); | |
1304 | } | |
1305 | ||
1306 | static void | |
1307 | fill_iface(char *which, union ip_fw_if *ifu, int *byname, int ac, char *arg) | |
1308 | { | |
1309 | if (!ac) | |
1310 | show_usage("missing argument for ``%s''", which); | |
1311 | ||
1312 | /* Parse the interface or address */ | |
1313 | if (!strcmp(arg, "any")) { | |
1314 | ifu->fu_via_ip.s_addr = 0; | |
1315 | *byname = 0; | |
1316 | } else if (!isdigit(*arg)) { | |
1317 | char *q; | |
1318 | ||
1319 | *byname = 1; | |
1320 | strncpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name)); | |
1321 | ifu->fu_via_if.name[sizeof(ifu->fu_via_if.name) - 1] = '\0'; | |
1322 | for (q = ifu->fu_via_if.name; | |
1323 | *q && !isdigit(*q) && *q != '*'; q++) | |
1324 | continue; | |
1325 | ifu->fu_via_if.unit = (*q == '*') ? -1 : atoi(q); | |
1326 | *q = '\0'; | |
1327 | verify_interface(ifu); | |
1328 | } else if (!inet_aton(arg, &ifu->fu_via_ip)) { | |
1329 | show_usage("bad ip address ``%s''", arg); | |
1330 | } else | |
1331 | *byname = 0; | |
1332 | } | |
1333 | ||
7ba0088d | 1334 | #ifdef DUMMYNET |
b7080c8e A |
1335 | static void |
1336 | config_pipe(int ac, char **av) | |
1337 | { | |
1338 | struct dn_pipe pipe; | |
7ba0088d A |
1339 | int i; |
1340 | char *end; | |
1341 | ||
b7080c8e | 1342 | memset(&pipe, 0, sizeof pipe); |
7ba0088d | 1343 | |
b7080c8e A |
1344 | av++; ac--; |
1345 | /* Pipe number */ | |
1346 | if (ac && isdigit(**av)) { | |
7ba0088d A |
1347 | i = atoi(*av); av++; ac--; |
1348 | if (do_pipe == 1) | |
1349 | pipe.pipe_nr = i; | |
1350 | else | |
1351 | pipe.fs.fs_nr = i; | |
b7080c8e A |
1352 | } |
1353 | while (ac > 1) { | |
7ba0088d A |
1354 | if (!strncmp(*av, "plr", strlen(*av))) { |
1355 | ||
b7080c8e | 1356 | double d = strtod(av[1], NULL); |
7ba0088d A |
1357 | if (d > 1) |
1358 | d = 1; | |
1359 | else if (d < 0) | |
1360 | d = 0; | |
1361 | pipe.fs.plr = (int)(d*0x7fffffff); | |
b7080c8e | 1362 | av+=2; ac-=2; |
7ba0088d A |
1363 | } else if (!strncmp(*av, "queue", strlen(*av))) { |
1364 | end = NULL; | |
1365 | pipe.fs.qsize = strtoul(av[1], &end, 0); | |
1366 | if (*end == 'K' || *end == 'k') { | |
1367 | pipe.fs.flags_fs |= DN_QSIZE_IS_BYTES; | |
1368 | pipe.fs.qsize *= 1024; | |
1369 | } else if (*end == 'B' || !strncmp(end, "by", 2)) { | |
1370 | pipe.fs.flags_fs |= DN_QSIZE_IS_BYTES; | |
b7080c8e A |
1371 | } |
1372 | av+=2; ac-=2; | |
7ba0088d A |
1373 | } else if (!strncmp(*av, "buckets", strlen(*av))) { |
1374 | pipe.fs.rq_size = strtoul(av[1], NULL, 0); | |
1375 | av+=2; ac-=2; | |
1376 | } else if (!strncmp(*av, "mask", strlen(*av))) { | |
1377 | /* per-flow queue, mask is dst_ip, dst_port, | |
1378 | * src_ip, src_port, proto measured in bits | |
1379 | */ | |
1380 | u_int32_t a; | |
1381 | u_int32_t *par = NULL; | |
1382 | ||
1383 | pipe.fs.flow_mask.dst_ip = 0; | |
1384 | pipe.fs.flow_mask.src_ip = 0; | |
1385 | pipe.fs.flow_mask.dst_port = 0; | |
1386 | pipe.fs.flow_mask.src_port = 0; | |
1387 | pipe.fs.flow_mask.proto = 0; | |
1388 | end = NULL; | |
1389 | av++; ac--; | |
1390 | if (ac >= 1 && !strncmp(*av, "all", strlen(*av))) { | |
1391 | /* special case -- all bits are significant */ | |
1392 | pipe.fs.flow_mask.dst_ip = ~0; | |
1393 | pipe.fs.flow_mask.src_ip = ~0; | |
1394 | pipe.fs.flow_mask.dst_port = ~0; | |
1395 | pipe.fs.flow_mask.src_port = ~0; | |
1396 | pipe.fs.flow_mask.proto = ~0; | |
1397 | pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK; | |
1398 | av++; ac--; | |
1399 | } else { | |
1400 | for (;;) { | |
1401 | if (ac < 1) | |
1402 | break; | |
1403 | if (!strncmp(*av, "dst-ip", strlen(*av))) | |
1404 | par = &(pipe.fs.flow_mask.dst_ip); | |
1405 | else if (!strncmp(*av, "src-ip", strlen(*av))) | |
1406 | par = &(pipe.fs.flow_mask.src_ip); | |
1407 | else if (!strncmp(*av, "dst-port", strlen(*av))) | |
1408 | (u_int16_t *)par = &(pipe.fs.flow_mask.dst_port); | |
1409 | else if (!strncmp(*av, "src-port", strlen(*av))) | |
1410 | (u_int16_t *)par = &(pipe.fs.flow_mask.src_port); | |
1411 | else if (!strncmp(*av, "proto", strlen(*av))) | |
1412 | (u_int8_t *)par = &(pipe.fs.flow_mask.proto); | |
1413 | else | |
1414 | break; | |
1415 | if (ac < 2) | |
1416 | show_usage("mask: %s value missing", *av); | |
1417 | if (*av[1] == '/') { | |
1418 | a = strtoul(av[1]+1, &end, 0); | |
1419 | if (a == 32) /* special case... */ | |
1420 | a = ~0; | |
1421 | else | |
1422 | a = (1 << a) - 1; | |
1423 | fprintf(stderr, " mask is 0x%08x\n", a); | |
1424 | } else | |
1425 | a = strtoul(av[1], &end, 0); | |
1426 | if ((u_int16_t *)par == &(pipe.fs.flow_mask.src_port) || | |
1427 | (u_int16_t *)par == &(pipe.fs.flow_mask.dst_port)) { | |
1428 | if (a >= (1<<16)) | |
1429 | show_usage("mask: %s must be 16 bit, not 0x%08x", | |
1430 | *av, a); | |
1431 | *((u_int16_t *)par) = (u_int16_t) a; | |
1432 | } else if ((u_int8_t *)par == &(pipe.fs.flow_mask.proto)) { | |
1433 | if (a >= (1<<8)) | |
1434 | show_usage("mask: %s must be 8 bit, not 0x%08x", | |
1435 | *av, a); | |
1436 | *((u_int8_t *)par) = (u_int8_t) a; | |
1437 | } else | |
1438 | *par = a; | |
1439 | if (a != 0) | |
1440 | pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK; | |
1441 | av += 2; ac -= 2; | |
1442 | } /* end for */ | |
1443 | } | |
1444 | } else if (!strncmp(*av, "red", strlen(*av)) || | |
1445 | !strncmp(*av, "gred", strlen(*av))) { /* RED enabled */ | |
1446 | pipe.fs.flags_fs |= DN_IS_RED; | |
1447 | if (*av[0] == 'g') | |
1448 | pipe.fs.flags_fs |= DN_IS_GENTLE_RED; | |
1449 | if ((end = strsep(&av[1],"/"))) { | |
1450 | double w_q = strtod(end, NULL); | |
1451 | if (w_q > 1 || w_q <= 0) | |
1452 | show_usage("w_q %f must be 0 < x <= 1", w_q); | |
1453 | pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED)); | |
1454 | } | |
1455 | if ((end = strsep(&av[1],"/"))) { | |
1456 | pipe.fs.min_th = strtoul(end, &end, 0); | |
1457 | if (*end == 'K' || *end == 'k') | |
1458 | pipe.fs.min_th *= 1024; | |
1459 | } | |
1460 | if ((end = strsep(&av[1],"/"))) { | |
1461 | pipe.fs.max_th = strtoul(end, &end, 0); | |
1462 | if (*end == 'K' || *end == 'k') | |
1463 | pipe.fs.max_th *= 1024; | |
1464 | } | |
1465 | if ((end = strsep(&av[1],"/"))) { | |
1466 | double max_p = strtod(end, NULL); | |
1467 | if (max_p > 1 || max_p <= 0) | |
1468 | show_usage("max_p %f must be 0 < x <= 1", max_p); | |
1469 | pipe.fs.max_p = (int) (max_p * (1 << SCALE_RED)); | |
1470 | } | |
1471 | av+=2; ac-=2; | |
1472 | } else if (!strncmp(*av, "droptail", strlen(*av))) { /* DROPTAIL */ | |
1473 | pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED); | |
1474 | av+=1; ac-=1; | |
1475 | } else { | |
1476 | if (do_pipe == 1) { | |
1477 | /* some commands are only good for pipes. */ | |
1478 | if (!strncmp(*av, "bw", strlen(*av)) || | |
1479 | ! strncmp(*av, "bandwidth", strlen(*av))) { | |
1480 | if (av[1][0] >= 'a' && av[1][0] <= 'z') { | |
1481 | int l = sizeof(pipe.if_name)-1; | |
1482 | /* interface name */ | |
1483 | strncpy(pipe.if_name, av[1], l); | |
1484 | pipe.if_name[l] = '\0'; | |
1485 | pipe.bandwidth = 0; | |
1486 | } else { | |
1487 | pipe.if_name[0] = '\0'; | |
1488 | pipe.bandwidth = strtoul(av[1], &end, 0); | |
1489 | if (*end == 'K' || *end == 'k') | |
1490 | end++, pipe.bandwidth *= 1000; | |
1491 | else if (*end == 'M') | |
1492 | end++, pipe.bandwidth *= 1000000; | |
1493 | if (*end == 'B' || !strncmp(end, "by", 2)) | |
1494 | pipe.bandwidth *= 8; | |
1495 | } | |
1496 | av+=2; ac-=2; | |
1497 | } else if (!strncmp(*av, "delay", strlen(*av))) { | |
1498 | pipe.delay = strtoul(av[1], NULL, 0); | |
1499 | av+=2; ac-=2; | |
1500 | } else | |
1501 | show_usage("unrecognised pipe option ``%s''", *av); | |
1502 | } else { /* this refers to a queue */ | |
1503 | if (!strncmp(*av, "weight", strlen(*av))) { | |
1504 | pipe.fs.weight = strtoul(av[1], &end, 0); | |
1505 | av += 2; | |
1506 | ac -= 2; | |
1507 | } else if (!strncmp(*av, "pipe", strlen(*av))) { | |
1508 | pipe.fs.parent_nr = strtoul(av[1], &end, 0); | |
1509 | av += 2; | |
1510 | ac -= 2; | |
b7080c8e A |
1511 | } else |
1512 | show_usage("unrecognised option ``%s''", *av); | |
1513 | } | |
7ba0088d A |
1514 | } |
1515 | } | |
1516 | if (do_pipe == 1) { | |
1517 | if (pipe.pipe_nr == 0) | |
1518 | show_usage("pipe_nr %d must be > 0", pipe.pipe_nr); | |
1519 | if (pipe.delay > 10000) | |
b7080c8e | 1520 | show_usage("delay %d must be < 10000", pipe.delay); |
7ba0088d A |
1521 | } else { /* do_pipe == 2, queue */ |
1522 | if (pipe.fs.parent_nr == 0) | |
1523 | show_usage("pipe %d must be > 0", pipe.fs.parent_nr); | |
1524 | if (pipe.fs.weight >100) | |
1525 | show_usage("weight %d must be <= 100", pipe.fs.weight); | |
1526 | } | |
1527 | if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) { | |
1528 | if (pipe.fs.qsize > 1024*1024) | |
1529 | show_usage("queue size %d, must be < 1MB", | |
1530 | pipe.fs.qsize); | |
1531 | } else { | |
1532 | if (pipe.fs.qsize > 100) | |
1533 | show_usage("queue size %d, must be 2 <= x <= 100", | |
1534 | pipe.fs.qsize); | |
1535 | } | |
1536 | if (pipe.fs.flags_fs & DN_IS_RED) { | |
1537 | if (pipe.fs.min_th >= pipe.fs.max_th) | |
1538 | show_usage("min_th %d must be < than max_th %d", | |
1539 | pipe.fs.min_th, pipe.fs.max_th); | |
1540 | if (pipe.fs.max_th == 0) | |
1541 | show_usage("max_th must be > 0"); | |
1542 | if (pipe.bandwidth) { | |
1543 | size_t len; | |
1544 | int lookup_depth, avg_pkt_size; | |
1545 | double s, idle, weight, w_q; | |
1546 | struct clockinfo clock; | |
1547 | int t; | |
1548 | ||
1549 | len = sizeof(int); | |
1550 | if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", | |
1551 | &lookup_depth, &len, NULL, 0) == -1) | |
1552 | ||
1553 | errx(1, "sysctlbyname(\"%s\")", | |
1554 | "net.inet.ip.dummynet.red_lookup_depth"); | |
1555 | if (lookup_depth == 0) | |
1556 | show_usage("net.inet.ip.dummynet.red_lookup_depth must" | |
1557 | "greater than zero"); | |
1558 | ||
1559 | len = sizeof(int); | |
1560 | if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", | |
1561 | &avg_pkt_size, &len, NULL, 0) == -1) | |
1562 | ||
1563 | errx(1, "sysctlbyname(\"%s\")", | |
1564 | "net.inet.ip.dummynet.red_avg_pkt_size"); | |
1565 | if (avg_pkt_size == 0) | |
1566 | show_usage("net.inet.ip.dummynet.red_avg_pkt_size must" | |
1567 | "greater than zero"); | |
1568 | ||
1569 | len = sizeof(struct clockinfo); | |
1570 | if (sysctlbyname("kern.clockrate", | |
1571 | &clock, &len, NULL, 0) == -1) | |
1572 | errx(1, "sysctlbyname(\"%s\")", "kern.clockrate"); | |
1573 | ||
1574 | /* ticks needed for sending a medium-sized packet */ | |
1575 | s = clock.hz * avg_pkt_size * 8 / pipe.bandwidth; | |
1576 | ||
1577 | /* | |
1578 | * max idle time (in ticks) before avg queue size becomes 0. | |
1579 | * NOTA: (3/w_q) is approx the value x so that | |
1580 | * (1-w_q)^x < 10^-3. | |
1581 | */ | |
1582 | w_q = ((double) pipe.fs.w_q) / (1 << SCALE_RED); | |
1583 | idle = s * 3. / w_q; | |
1584 | pipe.fs.lookup_step = (int) idle / lookup_depth; | |
1585 | if (!pipe.fs.lookup_step) | |
1586 | pipe.fs.lookup_step = 1; | |
1587 | weight = 1 - w_q; | |
1588 | for (t = pipe.fs.lookup_step; t > 0; --t) | |
1589 | weight *= weight; | |
1590 | pipe.fs.lookup_weight = (int) (weight * (1 << SCALE_RED)); | |
1591 | } | |
1592 | } | |
b7080c8e A |
1593 | #if 0 |
1594 | printf("configuring pipe %d bw %d delay %d size %d\n", | |
1595 | pipe.pipe_nr, pipe.bandwidth, pipe.delay, pipe.queue_size); | |
1596 | #endif | |
7ba0088d | 1597 | i = setsockopt(s,IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe, sizeof pipe); |
b7080c8e A |
1598 | if (i) |
1599 | err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); | |
7ba0088d | 1600 | |
b7080c8e | 1601 | } |
7ba0088d | 1602 | #endif /* DUMMYNET */ |
b7080c8e A |
1603 | |
1604 | static void | |
7ba0088d | 1605 | add(ac, av) |
b7080c8e A |
1606 | int ac; |
1607 | char **av; | |
1608 | { | |
1609 | struct ip_fw rule; | |
1610 | int i; | |
1611 | u_char proto; | |
1612 | struct protoent *pe; | |
1613 | int saw_xmrc = 0, saw_via = 0; | |
7ba0088d | 1614 | |
b7080c8e | 1615 | memset(&rule, 0, sizeof rule); |
7ba0088d | 1616 | rule.version = IP_FW_CURRENT_API_VERSION; |
b7080c8e A |
1617 | |
1618 | av++; ac--; | |
1619 | ||
1620 | /* Rule number */ | |
1621 | if (ac && isdigit(**av)) { | |
1622 | rule.fw_number = atoi(*av); av++; ac--; | |
1623 | } | |
1624 | ||
1625 | /* Action */ | |
7ba0088d A |
1626 | if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) { |
1627 | double d = strtod(av[1], NULL); | |
1628 | if (d <= 0 || d > 1) | |
1629 | show_usage("illegal match prob. %s", av[1]); | |
1630 | if (d != 1) { /* 1 means always match */ | |
1631 | rule.fw_flg |= IP_FW_F_RND_MATCH; | |
1632 | /* we really store dont_match probability */ | |
1633 | (long)rule.pipe_ptr = (long)((1 - d) * 0x7fffffff); | |
1634 | } | |
1635 | av += 2; ac -= 2; | |
1636 | } | |
1637 | ||
b7080c8e A |
1638 | if (ac == 0) |
1639 | show_usage("missing action"); | |
7ba0088d A |
1640 | if (!strncmp(*av, "accept", strlen(*av)) |
1641 | || !strncmp(*av, "pass" ,strlen(*av)) | |
1642 | || !strncmp(*av, "allow", strlen(*av)) | |
1643 | || !strncmp(*av, "permit", strlen(*av))) { | |
b7080c8e | 1644 | rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--; |
7ba0088d | 1645 | } else if (!strncmp(*av, "count", strlen(*av))) { |
b7080c8e | 1646 | rule.fw_flg |= IP_FW_F_COUNT; av++; ac--; |
7ba0088d A |
1647 | } |
1648 | #ifdef DUMMYNET | |
1649 | else if (!strncmp(*av, "pipe", strlen(*av))) { | |
b7080c8e A |
1650 | rule.fw_flg |= IP_FW_F_PIPE; av++; ac--; |
1651 | if (!ac) | |
1652 | show_usage("missing pipe number"); | |
1653 | rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; | |
7ba0088d A |
1654 | } else if (!strncmp(*av, "queue", strlen(*av))) { |
1655 | rule.fw_flg |= IP_FW_F_QUEUE; av++; ac--; | |
1656 | if (!ac) | |
1657 | show_usage("missing queue number"); | |
1658 | rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; | |
1659 | } | |
1660 | #endif /* DUMMYNET */ | |
1661 | else if (!strncmp(*av, "divert", strlen(*av))) { | |
b7080c8e A |
1662 | rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--; |
1663 | if (!ac) | |
1664 | show_usage("missing %s port", "divert"); | |
1665 | rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; | |
1666 | if (rule.fw_divert_port == 0) { | |
1667 | struct servent *s; | |
1668 | setservent(1); | |
1669 | s = getservbyname(av[-1], "divert"); | |
1670 | if (s != NULL) | |
1671 | rule.fw_divert_port = ntohs(s->s_port); | |
1672 | else | |
1673 | show_usage("illegal %s port", "divert"); | |
1674 | } | |
7ba0088d | 1675 | } else if (!strncmp(*av, "tee", strlen(*av))) { |
b7080c8e A |
1676 | rule.fw_flg |= IP_FW_F_TEE; av++; ac--; |
1677 | if (!ac) | |
1678 | show_usage("missing %s port", "tee divert"); | |
1679 | rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; | |
1680 | if (rule.fw_divert_port == 0) { | |
1681 | struct servent *s; | |
1682 | setservent(1); | |
1683 | s = getservbyname(av[-1], "divert"); | |
1684 | if (s != NULL) | |
1685 | rule.fw_divert_port = ntohs(s->s_port); | |
1686 | else | |
1687 | show_usage("illegal %s port", "tee divert"); | |
1688 | } | |
7ba0088d A |
1689 | } else if (!strncmp(*av, "fwd", strlen(*av)) || |
1690 | !strncmp(*av, "forward", strlen(*av))) { | |
b7080c8e A |
1691 | struct in_addr dummyip; |
1692 | char *pp; | |
1693 | rule.fw_flg |= IP_FW_F_FWD; av++; ac--; | |
1694 | if (!ac) | |
1695 | show_usage("missing forwarding IP address"); | |
1696 | rule.fw_fwd_ip.sin_len = sizeof(struct sockaddr_in); | |
1697 | rule.fw_fwd_ip.sin_family = AF_INET; | |
1698 | rule.fw_fwd_ip.sin_port = 0; | |
1699 | pp = strchr(*av, ':'); | |
1700 | if(pp == NULL) | |
1701 | pp = strchr(*av, ','); | |
1702 | if(pp != NULL) | |
1703 | { | |
1704 | *(pp++) = '\0'; | |
7ba0088d A |
1705 | i = lookup_port(pp, 0, 1, 0); |
1706 | if (i == -1) | |
1707 | show_usage("illegal forwarding port ``%s''", pp); | |
1708 | else | |
1709 | rule.fw_fwd_ip.sin_port = (u_short)i; | |
b7080c8e A |
1710 | } |
1711 | fill_ip(&(rule.fw_fwd_ip.sin_addr), &dummyip, &ac, &av); | |
1712 | if (rule.fw_fwd_ip.sin_addr.s_addr == 0) | |
1713 | show_usage("illegal forwarding IP address"); | |
1714 | ||
7ba0088d | 1715 | } else if (!strncmp(*av, "skipto", strlen(*av))) { |
b7080c8e A |
1716 | rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--; |
1717 | if (!ac) | |
1718 | show_usage("missing skipto rule number"); | |
1719 | rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--; | |
7ba0088d A |
1720 | } else if ((!strncmp(*av, "deny", strlen(*av)) |
1721 | || !strncmp(*av, "drop", strlen(*av)))) { | |
b7080c8e | 1722 | rule.fw_flg |= IP_FW_F_DENY; av++; ac--; |
7ba0088d | 1723 | } else if (!strncmp(*av, "reject", strlen(*av))) { |
b7080c8e A |
1724 | rule.fw_flg |= IP_FW_F_REJECT; av++; ac--; |
1725 | rule.fw_reject_code = ICMP_UNREACH_HOST; | |
7ba0088d | 1726 | } else if (!strncmp(*av, "reset", strlen(*av))) { |
b7080c8e A |
1727 | rule.fw_flg |= IP_FW_F_REJECT; av++; ac--; |
1728 | rule.fw_reject_code = IP_FW_REJECT_RST; /* check TCP later */ | |
7ba0088d | 1729 | } else if (!strncmp(*av, "unreach", strlen(*av))) { |
b7080c8e A |
1730 | rule.fw_flg |= IP_FW_F_REJECT; av++; ac--; |
1731 | fill_reject_code(&rule.fw_reject_code, *av); av++; ac--; | |
7ba0088d A |
1732 | } else if (!strncmp(*av, "check-state", strlen(*av))) { |
1733 | rule.fw_flg |= IP_FW_F_CHECK_S; av++; ac--; | |
1734 | goto done; | |
b7080c8e A |
1735 | } else { |
1736 | show_usage("invalid action ``%s''", *av); | |
1737 | } | |
1738 | ||
1739 | /* [log] */ | |
7ba0088d | 1740 | if (ac && !strncmp(*av, "log", strlen(*av))) { |
b7080c8e A |
1741 | rule.fw_flg |= IP_FW_F_PRN; av++; ac--; |
1742 | } | |
7ba0088d A |
1743 | if (ac && !strncmp(*av, "logamount", strlen(*av))) { |
1744 | if (!(rule.fw_flg & IP_FW_F_PRN)) | |
1745 | show_usage("``logamount'' not valid without ``log''"); | |
1746 | ac--; av++; | |
1747 | if (!ac) | |
1748 | show_usage("``logamount'' requires argument"); | |
1749 | rule.fw_logamount = atoi(*av); | |
1750 | if (rule.fw_logamount < 0) | |
1751 | show_usage("``logamount'' argument must be positive"); | |
1752 | if (rule.fw_logamount == 0) | |
1753 | rule.fw_logamount = -1; | |
1754 | ac--; av++; | |
1755 | } | |
b7080c8e A |
1756 | |
1757 | /* protocol */ | |
1758 | if (ac == 0) | |
1759 | show_usage("missing protocol"); | |
1760 | if ((proto = atoi(*av)) > 0) { | |
1761 | rule.fw_prot = proto; av++; ac--; | |
7ba0088d | 1762 | } else if (!strncmp(*av, "all", strlen(*av))) { |
b7080c8e A |
1763 | rule.fw_prot = IPPROTO_IP; av++; ac--; |
1764 | } else if ((pe = getprotobyname(*av)) != NULL) { | |
1765 | rule.fw_prot = pe->p_proto; av++; ac--; | |
1766 | } else { | |
1767 | show_usage("invalid protocol ``%s''", *av); | |
1768 | } | |
1769 | ||
1770 | if (rule.fw_prot != IPPROTO_TCP | |
1771 | && (rule.fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT | |
1772 | && rule.fw_reject_code == IP_FW_REJECT_RST) | |
1773 | show_usage("``reset'' is only valid for tcp packets"); | |
1774 | ||
1775 | /* from */ | |
7ba0088d | 1776 | if (ac && !strncmp(*av, "from", strlen(*av))) { av++; ac--; } |
b7080c8e A |
1777 | else |
1778 | show_usage("missing ``from''"); | |
1779 | ||
7ba0088d | 1780 | if (ac && !strncmp(*av, "not", strlen(*av))) { |
b7080c8e A |
1781 | rule.fw_flg |= IP_FW_F_INVSRC; |
1782 | av++; ac--; | |
1783 | } | |
1784 | if (!ac) | |
1785 | show_usage("missing arguments"); | |
1786 | ||
7ba0088d A |
1787 | if (ac && !strncmp(*av, "me", strlen(*av))) { |
1788 | rule.fw_flg |= IP_FW_F_SME; | |
1789 | av++; ac--; | |
1790 | } else { | |
1791 | fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av); | |
1792 | } | |
b7080c8e | 1793 | |
7ba0088d | 1794 | if (ac && (isdigit(**av) || lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) { |
b7080c8e | 1795 | u_short nports = 0; |
7ba0088d | 1796 | int retval; |
b7080c8e | 1797 | |
7ba0088d A |
1798 | retval = fill_port(&nports, rule.fw_uar.fw_pts, 0, *av, rule.fw_prot); |
1799 | if (retval == 1) | |
b7080c8e | 1800 | rule.fw_flg |= IP_FW_F_SRNG; |
7ba0088d A |
1801 | else if (retval == 2) |
1802 | rule.fw_flg |= IP_FW_F_SMSK; | |
b7080c8e A |
1803 | IP_FW_SETNSRCP(&rule, nports); |
1804 | av++; ac--; | |
1805 | } | |
1806 | ||
1807 | /* to */ | |
7ba0088d | 1808 | if (ac && !strncmp(*av, "to", strlen(*av))) { av++; ac--; } |
b7080c8e A |
1809 | else |
1810 | show_usage("missing ``to''"); | |
1811 | ||
7ba0088d | 1812 | if (ac && !strncmp(*av, "not", strlen(*av))) { |
b7080c8e A |
1813 | rule.fw_flg |= IP_FW_F_INVDST; |
1814 | av++; ac--; | |
1815 | } | |
1816 | if (!ac) | |
1817 | show_usage("missing arguments"); | |
1818 | ||
7ba0088d A |
1819 | if (ac && !strncmp(*av, "me", strlen(*av))) { |
1820 | rule.fw_flg |= IP_FW_F_DME; | |
1821 | av++; ac--; | |
1822 | } else { | |
1823 | fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av); | |
1824 | } | |
b7080c8e | 1825 | |
7ba0088d | 1826 | if (ac && (isdigit(**av) || lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) { |
b7080c8e | 1827 | u_short nports = 0; |
7ba0088d | 1828 | int retval; |
b7080c8e | 1829 | |
7ba0088d A |
1830 | retval = fill_port(&nports, |
1831 | rule.fw_uar.fw_pts, IP_FW_GETNSRCP(&rule), *av, rule.fw_prot); | |
1832 | if (retval == 1) | |
b7080c8e | 1833 | rule.fw_flg |= IP_FW_F_DRNG; |
7ba0088d A |
1834 | else if (retval == 2) |
1835 | rule.fw_flg |= IP_FW_F_DMSK; | |
b7080c8e A |
1836 | IP_FW_SETNDSTP(&rule, nports); |
1837 | av++; ac--; | |
1838 | } | |
1839 | ||
1840 | if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP) | |
1841 | && (IP_FW_GETNSRCP(&rule) || IP_FW_GETNDSTP(&rule))) { | |
1842 | show_usage("only TCP and UDP protocols are valid" | |
1843 | " with port specifications"); | |
1844 | } | |
1845 | ||
1846 | while (ac) { | |
7ba0088d A |
1847 | if (!strncmp(*av, "uid", strlen(*av))) { |
1848 | struct passwd *pwd; | |
1849 | char *end; | |
1850 | uid_t uid; | |
1851 | ||
1852 | rule.fw_flg |= IP_FW_F_UID; | |
1853 | ac--; av++; | |
1854 | if (!ac) | |
1855 | show_usage("``uid'' requires argument"); | |
1856 | ||
1857 | uid = strtoul(*av, &end, 0); | |
1858 | if (*end == '\0') | |
1859 | pwd = getpwuid(uid); | |
1860 | else | |
1861 | pwd = getpwnam(*av); | |
1862 | if (pwd == NULL) | |
1863 | show_usage("uid \"%s\" is nonexistant", *av); | |
1864 | rule.fw_uid = pwd->pw_uid; | |
1865 | ac--; av++; | |
1866 | continue; | |
1867 | } | |
1868 | if (!strncmp(*av, "in", strlen(*av))) { | |
b7080c8e A |
1869 | rule.fw_flg |= IP_FW_F_IN; |
1870 | av++; ac--; continue; | |
1871 | } | |
7ba0088d A |
1872 | if (!strncmp(*av, "keep-state", strlen(*av))) { |
1873 | u_long type; | |
1874 | rule.fw_flg |= IP_FW_F_KEEP_S; | |
1875 | ||
1876 | av++; ac--; | |
1877 | if (ac > 0 && (type = atoi(*av)) != 0) { | |
1878 | (int)rule.next_rule_ptr = type; | |
1879 | av++; ac--; | |
1880 | } | |
1881 | continue; | |
1882 | } | |
1883 | if (!strncmp(*av, "bridged", strlen(*av))) { | |
1884 | rule.fw_flg |= IP_FW_BRIDGED; | |
1885 | av++; ac--; continue; | |
1886 | } | |
1887 | if (!strncmp(*av, "out", strlen(*av))) { | |
b7080c8e A |
1888 | rule.fw_flg |= IP_FW_F_OUT; |
1889 | av++; ac--; continue; | |
1890 | } | |
7ba0088d | 1891 | if (ac && !strncmp(*av, "xmit", strlen(*av))) { |
b7080c8e A |
1892 | union ip_fw_if ifu; |
1893 | int byname; | |
1894 | ||
1895 | if (saw_via) { | |
1896 | badviacombo: | |
1897 | show_usage("``via'' is incompatible" | |
1898 | " with ``xmit'' and ``recv''"); | |
1899 | } | |
1900 | saw_xmrc = 1; | |
7ba0088d | 1901 | av++; ac--; |
b7080c8e A |
1902 | fill_iface("xmit", &ifu, &byname, ac, *av); |
1903 | rule.fw_out_if = ifu; | |
1904 | rule.fw_flg |= IP_FW_F_OIFACE; | |
1905 | if (byname) | |
1906 | rule.fw_flg |= IP_FW_F_OIFNAME; | |
1907 | av++; ac--; continue; | |
1908 | } | |
7ba0088d | 1909 | if (ac && !strncmp(*av, "recv", strlen(*av))) { |
b7080c8e A |
1910 | union ip_fw_if ifu; |
1911 | int byname; | |
1912 | ||
1913 | if (saw_via) | |
1914 | goto badviacombo; | |
1915 | saw_xmrc = 1; | |
7ba0088d | 1916 | av++; ac--; |
b7080c8e A |
1917 | fill_iface("recv", &ifu, &byname, ac, *av); |
1918 | rule.fw_in_if = ifu; | |
1919 | rule.fw_flg |= IP_FW_F_IIFACE; | |
1920 | if (byname) | |
1921 | rule.fw_flg |= IP_FW_F_IIFNAME; | |
1922 | av++; ac--; continue; | |
1923 | } | |
7ba0088d | 1924 | if (ac && !strncmp(*av, "via", strlen(*av))) { |
b7080c8e A |
1925 | union ip_fw_if ifu; |
1926 | int byname = 0; | |
1927 | ||
1928 | if (saw_xmrc) | |
1929 | goto badviacombo; | |
1930 | saw_via = 1; | |
7ba0088d | 1931 | av++; ac--; |
b7080c8e A |
1932 | fill_iface("via", &ifu, &byname, ac, *av); |
1933 | rule.fw_out_if = rule.fw_in_if = ifu; | |
1934 | if (byname) | |
1935 | rule.fw_flg |= | |
1936 | (IP_FW_F_IIFNAME | IP_FW_F_OIFNAME); | |
1937 | av++; ac--; continue; | |
1938 | } | |
7ba0088d | 1939 | if (!strncmp(*av, "fragment", strlen(*av))) { |
b7080c8e A |
1940 | rule.fw_flg |= IP_FW_F_FRAG; |
1941 | av++; ac--; continue; | |
1942 | } | |
7ba0088d A |
1943 | if (!strncmp(*av, "ipoptions", strlen(*av))) { |
1944 | av++; ac--; | |
b7080c8e A |
1945 | if (!ac) |
1946 | show_usage("missing argument" | |
1947 | " for ``ipoptions''"); | |
1948 | fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av); | |
1949 | av++; ac--; continue; | |
1950 | } | |
1951 | if (rule.fw_prot == IPPROTO_TCP) { | |
7ba0088d A |
1952 | if (!strncmp(*av, "established", strlen(*av))) { |
1953 | rule.fw_ipflg |= IP_FW_IF_TCPEST; | |
b7080c8e A |
1954 | av++; ac--; continue; |
1955 | } | |
7ba0088d | 1956 | if (!strncmp(*av, "setup", strlen(*av))) { |
b7080c8e A |
1957 | rule.fw_tcpf |= IP_FW_TCPF_SYN; |
1958 | rule.fw_tcpnf |= IP_FW_TCPF_ACK; | |
1959 | av++; ac--; continue; | |
1960 | } | |
7ba0088d A |
1961 | if (!strncmp(*av, "tcpflags", strlen(*av)) || |
1962 | !strncmp(*av, "tcpflgs", strlen(*av))) { | |
1963 | av++; ac--; | |
b7080c8e A |
1964 | if (!ac) |
1965 | show_usage("missing argument" | |
1966 | " for ``tcpflags''"); | |
1967 | fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av); | |
1968 | av++; ac--; continue; | |
1969 | } | |
7ba0088d A |
1970 | if (!strncmp(*av, "tcpoptions", strlen(*av)) || |
1971 | !strncmp(*av, "tcpopts", strlen(*av))) { | |
1972 | av++; ac--; | |
1973 | if (!ac) | |
1974 | show_usage("missing argument" | |
1975 | " for ``tcpoptions''"); | |
1976 | fill_tcpopts(&rule.fw_tcpopt, &rule.fw_tcpnopt, av); | |
1977 | av++; ac--; continue; | |
1978 | } | |
b7080c8e A |
1979 | } |
1980 | if (rule.fw_prot == IPPROTO_ICMP) { | |
7ba0088d | 1981 | if (!strncmp(*av, "icmptypes", strlen(*av))) { |
b7080c8e A |
1982 | av++; ac--; |
1983 | if (!ac) | |
1984 | show_usage("missing argument" | |
1985 | " for ``icmptypes''"); | |
1986 | fill_icmptypes(rule.fw_uar.fw_icmptypes, | |
1987 | av, &rule.fw_flg); | |
1988 | av++; ac--; continue; | |
1989 | } | |
1990 | } | |
1991 | show_usage("unknown argument ``%s''", *av); | |
1992 | } | |
1993 | ||
1994 | /* No direction specified -> do both directions */ | |
1995 | if (!(rule.fw_flg & (IP_FW_F_OUT|IP_FW_F_IN))) | |
1996 | rule.fw_flg |= (IP_FW_F_OUT|IP_FW_F_IN); | |
1997 | ||
1998 | /* Sanity check interface check, but handle "via" case separately */ | |
1999 | if (saw_via) { | |
2000 | if (rule.fw_flg & IP_FW_F_IN) | |
2001 | rule.fw_flg |= IP_FW_F_IIFACE; | |
2002 | if (rule.fw_flg & IP_FW_F_OUT) | |
2003 | rule.fw_flg |= IP_FW_F_OIFACE; | |
2004 | } else if ((rule.fw_flg & IP_FW_F_OIFACE) && (rule.fw_flg & IP_FW_F_IN)) | |
2005 | show_usage("can't check xmit interface of incoming packets"); | |
2006 | ||
2007 | /* frag may not be used in conjunction with ports or TCP flags */ | |
2008 | if (rule.fw_flg & IP_FW_F_FRAG) { | |
2009 | if (rule.fw_tcpf || rule.fw_tcpnf) | |
2010 | show_usage("can't mix 'frag' and tcpflags"); | |
2011 | ||
2012 | if (rule.fw_nports) | |
2013 | show_usage("can't mix 'frag' and port specifications"); | |
2014 | } | |
7ba0088d A |
2015 | if (rule.fw_flg & IP_FW_F_PRN) { |
2016 | if (!rule.fw_logamount) { | |
2017 | size_t len = sizeof(int); | |
2018 | ||
2019 | if (sysctlbyname("net.inet.ip.fw.verbose_limit", | |
2020 | &rule.fw_logamount, &len, NULL, 0) == -1) | |
2021 | errx(1, "sysctlbyname(\"%s\")", | |
2022 | "net.inet.ip.fw.verbose_limit"); | |
2023 | } else if (rule.fw_logamount == -1) | |
2024 | rule.fw_logamount = 0; | |
2025 | rule.fw_loghighest = rule.fw_logamount; | |
2026 | } | |
2027 | done: | |
2028 | i = sizeof(rule); | |
2029 | if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, &i) == -1) | |
2030 | err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD"); | |
b7080c8e A |
2031 | if (!do_quiet) |
2032 | show_ipfw(&rule, 10, 10); | |
b7080c8e A |
2033 | } |
2034 | ||
2035 | static void | |
2036 | zero (ac, av) | |
2037 | int ac; | |
2038 | char **av; | |
2039 | { | |
7ba0088d A |
2040 | struct ip_fw rule; |
2041 | memset(&rule, 0, sizeof rule); | |
2042 | rule.version = IP_FW_CURRENT_API_VERSION; | |
2043 | ||
b7080c8e A |
2044 | av++; ac--; |
2045 | ||
2046 | if (!ac) { | |
2047 | /* clear all entries */ | |
7ba0088d | 2048 | if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, &rule, sizeof rule) < 0) |
b7080c8e A |
2049 | err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO"); |
2050 | if (!do_quiet) | |
2051 | printf("Accounting cleared.\n"); | |
2052 | } else { | |
b7080c8e A |
2053 | int failed = EX_OK; |
2054 | ||
b7080c8e A |
2055 | while (ac) { |
2056 | /* Rule number */ | |
2057 | if (isdigit(**av)) { | |
2058 | rule.fw_number = atoi(*av); av++; ac--; | |
2059 | if (setsockopt(s, IPPROTO_IP, | |
2060 | IP_FW_ZERO, &rule, sizeof rule)) { | |
2061 | warn("rule %u: setsockopt(%s)", rule.fw_number, | |
2062 | "IP_FW_ZERO"); | |
2063 | failed = EX_UNAVAILABLE; | |
2064 | } | |
2065 | else if (!do_quiet) | |
2066 | printf("Entry %d cleared\n", | |
2067 | rule.fw_number); | |
2068 | } else | |
2069 | show_usage("invalid rule number ``%s''", *av); | |
2070 | } | |
2071 | if (failed != EX_OK) | |
2072 | exit(failed); | |
2073 | } | |
2074 | } | |
2075 | ||
7ba0088d A |
2076 | static void |
2077 | resetlog (ac, av) | |
2078 | int ac; | |
2079 | char **av; | |
2080 | { | |
2081 | struct ip_fw rule; | |
2082 | memset(&rule, 0, sizeof rule); | |
2083 | rule.version = IP_FW_CURRENT_API_VERSION; | |
2084 | ||
2085 | av++; ac--; | |
2086 | ||
2087 | if (!ac) { | |
2088 | /* clear all entries */ | |
2089 | if (setsockopt(s, IPPROTO_IP, IP_FW_RESETLOG, &rule, sizeof rule) < 0) | |
2090 | err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_RESETLOG"); | |
2091 | if (!do_quiet) | |
2092 | printf("Logging counts reset.\n"); | |
2093 | } else { | |
2094 | int failed = EX_OK; | |
2095 | ||
2096 | while (ac) { | |
2097 | /* Rule number */ | |
2098 | if (isdigit(**av)) { | |
2099 | rule.fw_number = atoi(*av); av++; ac--; | |
2100 | if (setsockopt(s, IPPROTO_IP, | |
2101 | IP_FW_RESETLOG, &rule, sizeof rule)) { | |
2102 | warn("rule %u: setsockopt(%s)", rule.fw_number, | |
2103 | "IP_FW_RESETLOG"); | |
2104 | failed = EX_UNAVAILABLE; | |
2105 | } | |
2106 | else if (!do_quiet) | |
2107 | printf("Entry %d logging count reset\n", | |
2108 | rule.fw_number); | |
2109 | } else | |
2110 | show_usage("invalid rule number ``%s''", *av); | |
2111 | } | |
2112 | if (failed != EX_OK) | |
2113 | exit(failed); | |
2114 | } | |
2115 | } | |
2116 | ||
b7080c8e | 2117 | static int |
7ba0088d | 2118 | ipfw_main(ac, av) |
b7080c8e A |
2119 | int ac; |
2120 | char **av; | |
2121 | { | |
2122 | ||
2123 | int ch; | |
b7080c8e | 2124 | |
7ba0088d | 2125 | if (ac == 1) { |
b7080c8e A |
2126 | show_usage(NULL); |
2127 | } | |
2128 | ||
2129 | /* Set the force flag for non-interactive processes */ | |
2130 | do_force = !isatty(STDIN_FILENO); | |
2131 | ||
2132 | optind = optreset = 1; | |
7ba0088d | 2133 | while ((ch = getopt(ac, av, "s:afqtvN")) != -1) |
b7080c8e | 2134 | switch(ch) { |
7ba0088d A |
2135 | case 's': /* sort */ |
2136 | do_sort= atoi(optarg); | |
2137 | break; | |
b7080c8e | 2138 | case 'a': |
7ba0088d | 2139 | do_acct = 1; |
b7080c8e A |
2140 | break; |
2141 | case 'f': | |
7ba0088d | 2142 | do_force = 1; |
b7080c8e A |
2143 | break; |
2144 | case 'q': | |
7ba0088d | 2145 | do_quiet = 1; |
b7080c8e A |
2146 | break; |
2147 | case 't': | |
7ba0088d A |
2148 | do_time = 1; |
2149 | break; | |
2150 | case 'v': /* verbose */ | |
2151 | verbose++; | |
b7080c8e A |
2152 | break; |
2153 | case 'N': | |
7ba0088d | 2154 | do_resolv = 1; |
b7080c8e A |
2155 | break; |
2156 | default: | |
2157 | show_usage(NULL); | |
2158 | } | |
2159 | ||
2160 | ac -= optind; | |
2161 | if (*(av+=optind)==NULL) { | |
7ba0088d | 2162 | show_usage("bad arguments"); |
b7080c8e A |
2163 | } |
2164 | ||
7ba0088d A |
2165 | #ifdef DUMMYNET |
2166 | if (!strncmp(*av, "pipe", strlen(*av))) { | |
2167 | do_pipe = 1; | |
2168 | ac--; | |
2169 | av++; | |
2170 | } else if (!strncmp(*av, "queue", strlen(*av))) { | |
2171 | do_pipe = 2; | |
2172 | ac--; | |
2173 | av++; | |
2174 | } | |
b7080c8e A |
2175 | if (!ac) { |
2176 | show_usage("pipe requires arguments"); | |
2177 | } | |
7ba0088d A |
2178 | #endif /* DUMMYNET */ |
2179 | ||
2180 | /* allow argument swapping */ | |
2181 | if (ac > 1 && *av[0] >= '0' && *av[0] <= '9') { | |
2182 | char *p = av[0]; | |
2183 | av[0] = av[1]; | |
2184 | av[1] = p; | |
2185 | } | |
b7080c8e | 2186 | if (!strncmp(*av, "add", strlen(*av))) { |
7ba0088d A |
2187 | add(ac, av); |
2188 | } | |
2189 | #ifdef DUMMYNET | |
2190 | else if (do_pipe && !strncmp(*av, "config", strlen(*av))) { | |
2191 | config_pipe(ac, av); | |
2192 | } | |
2193 | #endif /* DUMMYNET */ | |
2194 | else if (!strncmp(*av, "delete", strlen(*av))) { | |
2195 | delete(ac, av); | |
b7080c8e A |
2196 | } else if (!strncmp(*av, "flush", strlen(*av))) { |
2197 | int do_flush = 0; | |
2198 | ||
7ba0088d | 2199 | if (do_force || do_quiet) |
b7080c8e A |
2200 | do_flush = 1; |
2201 | else { | |
2202 | int c; | |
2203 | ||
2204 | /* Ask the user */ | |
2205 | printf("Are you sure? [yn] "); | |
7ba0088d | 2206 | fflush(stdout); |
b7080c8e | 2207 | do { |
b7080c8e A |
2208 | c = toupper(getc(stdin)); |
2209 | while (c != '\n' && getc(stdin) != '\n') | |
2210 | if (feof(stdin)) | |
2211 | return (0); | |
2212 | } while (c != 'Y' && c != 'N'); | |
2213 | printf("\n"); | |
7ba0088d | 2214 | if (c == 'Y') |
b7080c8e A |
2215 | do_flush = 1; |
2216 | } | |
7ba0088d A |
2217 | if (do_flush) { |
2218 | int error = 0; | |
2219 | ||
2220 | #ifdef DUMMYNET | |
2221 | if (do_pipe) | |
2222 | { | |
2223 | error = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_FLUSH, NULL, 0); | |
2224 | } | |
2225 | else | |
2226 | #endif /* DUMMYNET */ | |
2227 | { | |
2228 | struct ip_fw rule; | |
2229 | memset(&rule, 0, sizeof rule); | |
2230 | rule.version = IP_FW_CURRENT_API_VERSION; | |
2231 | error = setsockopt(s, IPPROTO_IP, IP_FW_FLUSH, &rule, sizeof rule); | |
2232 | } | |
2233 | ||
2234 | if (error < 0) | |
2235 | #ifdef DUMMYNET | |
2236 | err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)", | |
2237 | do_pipe ? "DUMMYNET" : "FW"); | |
2238 | #else /* DUMMYNET */ | |
2239 | err(EX_UNAVAILABLE, "setsockopt(IP_FW_FLUSH)"); | |
2240 | #endif /* DUMMYNET */ | |
b7080c8e | 2241 | if (!do_quiet) |
7ba0088d A |
2242 | #ifdef DUMMYNET |
2243 | printf("Flushed all %s.\n", | |
2244 | do_pipe ? "pipes" : "rules"); | |
2245 | #else /* DUMMYNET */ | |
b7080c8e | 2246 | printf("Flushed all rules.\n"); |
7ba0088d | 2247 | #endif /* DUMMYNET */ |
b7080c8e A |
2248 | } |
2249 | } else if (!strncmp(*av, "zero", strlen(*av))) { | |
7ba0088d A |
2250 | zero(ac, av); |
2251 | } else if (!strncmp(*av, "resetlog", strlen(*av))) { | |
2252 | resetlog(ac, av); | |
b7080c8e | 2253 | } else if (!strncmp(*av, "print", strlen(*av))) { |
7ba0088d | 2254 | list(--ac, ++av); |
b7080c8e | 2255 | } else if (!strncmp(*av, "list", strlen(*av))) { |
7ba0088d | 2256 | list(--ac, ++av); |
b7080c8e A |
2257 | } else if (!strncmp(*av, "show", strlen(*av))) { |
2258 | do_acct++; | |
7ba0088d | 2259 | list(--ac, ++av); |
b7080c8e | 2260 | } else { |
7ba0088d | 2261 | show_usage("bad arguments"); |
b7080c8e A |
2262 | } |
2263 | return 0; | |
2264 | } | |
2265 | ||
7ba0088d | 2266 | int |
b7080c8e A |
2267 | main(ac, av) |
2268 | int ac; | |
2269 | char **av; | |
2270 | { | |
2271 | #define MAX_ARGS 32 | |
2272 | #define WHITESP " \t\f\v\n\r" | |
2273 | char buf[BUFSIZ]; | |
2274 | char *a, *p, *args[MAX_ARGS], *cmd = NULL; | |
2275 | char linename[10]; | |
7ba0088d | 2276 | int i, c, lineno, qflag, pflag, status; |
b7080c8e A |
2277 | FILE *f = NULL; |
2278 | pid_t preproc = 0; | |
2279 | ||
7ba0088d A |
2280 | s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); |
2281 | if (s < 0) | |
b7080c8e A |
2282 | err(EX_UNAVAILABLE, "socket"); |
2283 | ||
7ba0088d | 2284 | setbuf(stdout, 0); |
b7080c8e | 2285 | |
7ba0088d A |
2286 | /* |
2287 | * this is a nasty check on the last argument!!! | |
2288 | * If there happens to be a filename matching a keyword in the current | |
2289 | * directory, things will fail miserably. | |
2290 | */ | |
2291 | ||
2292 | if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) { | |
b7080c8e A |
2293 | qflag = pflag = i = 0; |
2294 | lineno = 0; | |
2295 | ||
2296 | while ((c = getopt(ac, av, "D:U:p:q")) != -1) | |
2297 | switch(c) { | |
2298 | case 'D': | |
2299 | if (!pflag) | |
2300 | errx(EX_USAGE, "-D requires -p"); | |
2301 | if (i > MAX_ARGS - 2) | |
2302 | errx(EX_USAGE, | |
2303 | "too many -D or -U options"); | |
2304 | args[i++] = "-D"; | |
2305 | args[i++] = optarg; | |
2306 | break; | |
2307 | ||
2308 | case 'U': | |
2309 | if (!pflag) | |
2310 | errx(EX_USAGE, "-U requires -p"); | |
2311 | if (i > MAX_ARGS - 2) | |
2312 | errx(EX_USAGE, | |
2313 | "too many -D or -U options"); | |
2314 | args[i++] = "-U"; | |
2315 | args[i++] = optarg; | |
2316 | break; | |
2317 | ||
2318 | case 'p': | |
2319 | pflag = 1; | |
2320 | cmd = optarg; | |
2321 | args[0] = cmd; | |
2322 | i = 1; | |
2323 | break; | |
2324 | ||
2325 | case 'q': | |
2326 | qflag = 1; | |
2327 | break; | |
2328 | ||
2329 | default: | |
2330 | show_usage(NULL); | |
2331 | } | |
2332 | ||
2333 | av += optind; | |
2334 | ac -= optind; | |
2335 | if (ac != 1) | |
2336 | show_usage("extraneous filename arguments"); | |
2337 | ||
2338 | if ((f = fopen(av[0], "r")) == NULL) | |
2339 | err(EX_UNAVAILABLE, "fopen: %s", av[0]); | |
2340 | ||
2341 | if (pflag) { | |
2342 | /* pipe through preprocessor (cpp or m4) */ | |
2343 | int pipedes[2]; | |
2344 | ||
2345 | args[i] = 0; | |
2346 | ||
2347 | if (pipe(pipedes) == -1) | |
2348 | err(EX_OSERR, "cannot create pipe"); | |
2349 | ||
2350 | switch((preproc = fork())) { | |
2351 | case -1: | |
2352 | err(EX_OSERR, "cannot fork"); | |
2353 | ||
2354 | case 0: | |
2355 | /* child */ | |
7ba0088d A |
2356 | if (dup2(fileno(f), 0) == -1 |
2357 | || dup2(pipedes[1], 1) == -1) | |
b7080c8e A |
2358 | err(EX_OSERR, "dup2()"); |
2359 | fclose(f); | |
2360 | close(pipedes[1]); | |
2361 | close(pipedes[0]); | |
2362 | execvp(cmd, args); | |
2363 | err(EX_OSERR, "execvp(%s) failed", cmd); | |
2364 | ||
2365 | default: | |
2366 | /* parent */ | |
2367 | fclose(f); | |
2368 | close(pipedes[1]); | |
2369 | if ((f = fdopen(pipedes[0], "r")) == NULL) { | |
2370 | int savederrno = errno; | |
2371 | ||
2372 | (void)kill(preproc, SIGTERM); | |
2373 | errno = savederrno; | |
2374 | err(EX_OSERR, "fdopen()"); | |
2375 | } | |
2376 | } | |
2377 | } | |
2378 | ||
2379 | while (fgets(buf, BUFSIZ, f)) { | |
2380 | lineno++; | |
2381 | sprintf(linename, "Line %d", lineno); | |
2382 | args[0] = linename; | |
2383 | ||
2384 | if (*buf == '#') | |
2385 | continue; | |
2386 | if ((p = strchr(buf, '#')) != NULL) | |
2387 | *p = '\0'; | |
7ba0088d A |
2388 | i = 1; |
2389 | if (qflag) | |
2390 | args[i++] = "-q"; | |
b7080c8e A |
2391 | for (a = strtok(buf, WHITESP); |
2392 | a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++) | |
2393 | args[i] = a; | |
2394 | if (i == (qflag? 2: 1)) | |
2395 | continue; | |
2396 | if (i == MAX_ARGS) | |
7ba0088d A |
2397 | errx(EX_USAGE, "%s: too many arguments", |
2398 | linename); | |
b7080c8e A |
2399 | args[i] = NULL; |
2400 | ||
7ba0088d | 2401 | ipfw_main(i, args); |
b7080c8e A |
2402 | } |
2403 | fclose(f); | |
2404 | if (pflag) { | |
7ba0088d A |
2405 | if (waitpid(preproc, &status, 0) == -1) |
2406 | errx(EX_OSERR, "waitpid()"); | |
2407 | if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) | |
2408 | errx(EX_UNAVAILABLE, | |
2409 | "preprocessor exited with status %d", | |
2410 | WEXITSTATUS(status)); | |
2411 | else if (WIFSIGNALED(status)) | |
2412 | errx(EX_UNAVAILABLE, | |
2413 | "preprocessor exited with signal %d", | |
2414 | WTERMSIG(status)); | |
b7080c8e | 2415 | } |
7ba0088d A |
2416 | } else { |
2417 | ipfw_main(ac, av); | |
2418 | } | |
b7080c8e A |
2419 | return EX_OK; |
2420 | } |