1
2 /*
3 * fw-ipf.c
4 *
5 * Copyright (c) 2001 Dug Song <dugsong@monkey.org>
6 *
7 * $Id$
8 */
9
10 #include "config.h"
11
12 #include <sys/param.h>
13 #include <sys/types.h>
14 #include <sys/ioctl.h>
15 #include <sys/socket.h>
16
17 #include <net/if.h>
18 #define _NETINET_IP6_H_ /* XXX */
19 #include <netinet/in.h>
20 #define ip_t ipf_ip_t
21 #ifdef HAVE_NETINET_IP_FIL_COMPAT_H
22 # include <netinet/ip_fil_compat.h>
23 #else
24 # include <netinet/ip_compat.h>
25 #endif
26 #include <netinet/ip_fil.h>
27 #undef ip_t
28
29 #include <assert.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #define KMEM_NAME "/dev/kmem"
38
39 #include "dnet.h"
40
41 #if !defined(fi_saddr) && !defined(fi_daddr)
42 # define fi_saddr fi_src.s_addr
43 # define fi_daddr fi_dst.s_addr
44 #endif
45
46 struct fw_handle {
47 int fd;
48 int kfd;
49 };
50
51 static void
rule_to_ipf(const struct fw_rule * rule,struct frentry * fr)52 rule_to_ipf(const struct fw_rule *rule, struct frentry *fr)
53 {
54 memset(fr, 0, sizeof(*fr));
55
56 if (*rule->fw_device != '\0') {
57 strlcpy(fr->fr_ifname, rule->fw_device, IFNAMSIZ);
58 strlcpy(fr->fr_oifname, rule->fw_device, IFNAMSIZ);
59 }
60 if (rule->fw_op == FW_OP_ALLOW)
61 fr->fr_flags |= FR_PASS;
62 else
63 fr->fr_flags |= FR_BLOCK;
64
65 if (rule->fw_dir == FW_DIR_IN)
66 fr->fr_flags |= FR_INQUE;
67 else
68 fr->fr_flags |= FR_OUTQUE;
69
70 fr->fr_ip.fi_p = rule->fw_proto;
71 fr->fr_ip.fi_saddr = rule->fw_src.addr_ip;
72 fr->fr_ip.fi_daddr = rule->fw_dst.addr_ip;
73 addr_btom(rule->fw_src.addr_bits, &fr->fr_mip.fi_saddr, IP_ADDR_LEN);
74 addr_btom(rule->fw_dst.addr_bits, &fr->fr_mip.fi_daddr, IP_ADDR_LEN);
75
76 switch (rule->fw_proto) {
77 case IPPROTO_ICMP:
78 fr->fr_icmpm = rule->fw_sport[1] << 8 |
79 (rule->fw_dport[1] & 0xff);
80 fr->fr_icmp = rule->fw_sport[0] << 8 |
81 (rule->fw_dport[0] & 0xff);
82 break;
83 case IPPROTO_TCP:
84 case IPPROTO_UDP:
85 fr->fr_sport = rule->fw_sport[0];
86 if (rule->fw_sport[0] != rule->fw_sport[1]) {
87 fr->fr_scmp = FR_INRANGE;
88 fr->fr_stop = rule->fw_sport[1];
89 } else
90 fr->fr_scmp = FR_EQUAL;
91
92 fr->fr_dport = rule->fw_dport[0];
93 if (rule->fw_dport[0] != rule->fw_dport[1]) {
94 fr->fr_dcmp = FR_INRANGE;
95 fr->fr_dtop = rule->fw_dport[1];
96 } else
97 fr->fr_dcmp = FR_EQUAL;
98 break;
99 }
100 }
101
102 static void
ipf_ports_to_rule(uint8_t cmp,uint16_t port,uint16_t top,uint16_t * range)103 ipf_ports_to_rule(uint8_t cmp, uint16_t port, uint16_t top, uint16_t *range)
104 {
105 switch (cmp) {
106 case FR_EQUAL:
107 range[0] = range[1] = port;
108 break;
109 case FR_NEQUAL:
110 range[0] = port - 1;
111 range[1] = port + 1;
112 break;
113 case FR_LESST:
114 range[0] = 0;
115 range[1] = port - 1;
116 break;
117 case FR_GREATERT:
118 range[0] = port + 1;
119 range[1] = TCP_PORT_MAX;
120 break;
121 case FR_LESSTE:
122 range[0] = 0;
123 range[1] = port;
124 break;
125 case FR_GREATERTE:
126 range[0] = port;
127 range[1] = TCP_PORT_MAX;
128 break;
129 case FR_OUTRANGE:
130 range[0] = port;
131 range[1] = top;
132 break;
133 case FR_INRANGE:
134 range[0] = port;
135 range[1] = top;
136 break;
137 default:
138 range[0] = 0;
139 range[1] = TCP_PORT_MAX;
140 }
141 }
142
143 static void
ipf_to_rule(const struct frentry * fr,struct fw_rule * rule)144 ipf_to_rule(const struct frentry *fr, struct fw_rule *rule)
145 {
146 memset(rule, 0, sizeof(*rule));
147
148 strlcpy(rule->fw_device, fr->fr_ifname, sizeof(rule->fw_device));
149 rule->fw_op = (fr->fr_flags & FR_PASS) ? FW_OP_ALLOW : FW_OP_BLOCK;
150 rule->fw_dir = (fr->fr_flags & FR_INQUE) ? FW_DIR_IN : FW_DIR_OUT;
151 rule->fw_proto = fr->fr_ip.fi_p;
152
153 rule->fw_src.addr_type = rule->fw_dst.addr_type = ADDR_TYPE_IP;
154 rule->fw_src.addr_ip = fr->fr_ip.fi_saddr;
155 rule->fw_dst.addr_ip = fr->fr_ip.fi_daddr;
156 addr_mtob(&fr->fr_mip.fi_saddr, IP_ADDR_LEN,
157 &rule->fw_src.addr_bits);
158 addr_mtob(&fr->fr_mip.fi_daddr, IP_ADDR_LEN,
159 &rule->fw_dst.addr_bits);
160
161 switch (rule->fw_proto) {
162 case IPPROTO_ICMP:
163 rule->fw_sport[0] = ntohs(fr->fr_icmp & fr->fr_icmpm) >> 8;
164 rule->fw_sport[1] = ntohs(fr->fr_icmpm) >> 8;
165 rule->fw_dport[0] = ntohs(fr->fr_icmp & fr->fr_icmpm) & 0xff;
166 rule->fw_dport[1] = ntohs(fr->fr_icmpm) & 0xff;
167 break;
168 case IPPROTO_TCP:
169 case IPPROTO_UDP:
170 ipf_ports_to_rule(fr->fr_scmp, fr->fr_sport,
171 fr->fr_stop, rule->fw_sport);
172 ipf_ports_to_rule(fr->fr_dcmp, fr->fr_dport,
173 fr->fr_dtop, rule->fw_dport);
174 break;
175 }
176 }
177
178 fw_t *
fw_open(void)179 fw_open(void)
180 {
181 fw_t *fw;
182
183 if ((fw = calloc(1, sizeof(*fw))) != NULL) {
184 fw->fd = fw->kfd = -1;
185 if ((fw->fd = open(IPL_NAME, O_RDWR, 0)) < 0)
186 return (fw_close(fw));
187 if ((fw->kfd = open(KMEM_NAME, O_RDONLY)) < 0)
188 return (fw_close(fw));
189 }
190 return (fw);
191 }
192
193 int
fw_add(fw_t * fw,const struct fw_rule * rule)194 fw_add(fw_t *fw, const struct fw_rule *rule)
195 {
196 struct frentry fr;
197
198 assert(fw != NULL && rule != NULL);
199
200 rule_to_ipf(rule, &fr);
201
202 return (ioctl(fw->fd, SIOCADDFR, &fr));
203 }
204
205 int
fw_delete(fw_t * fw,const struct fw_rule * rule)206 fw_delete(fw_t *fw, const struct fw_rule *rule)
207 {
208 struct frentry fr;
209
210 assert(fw != NULL && rule != NULL);
211
212 rule_to_ipf(rule, &fr);
213
214 return (ioctl(fw->fd, SIOCDELFR, &fr));
215 }
216
217 static int
fw_kcopy(fw_t * fw,u_char * buf,off_t pos,size_t n)218 fw_kcopy(fw_t *fw, u_char *buf, off_t pos, size_t n)
219 {
220 int i;
221
222 if (lseek(fw->kfd, pos, 0) < 0)
223 return (-1);
224
225 while ((i = read(fw->kfd, buf, n)) < n) {
226 if (i <= 0)
227 return (-1);
228 buf += i;
229 n -= i;
230 }
231 return (0);
232 }
233
234 int
fw_loop(fw_t * fw,fw_handler callback,void * arg)235 fw_loop(fw_t *fw, fw_handler callback, void *arg)
236 {
237 struct friostat fio;
238 struct friostat *fiop = &fio;
239 struct frentry *frp, fr;
240 struct fw_rule rule;
241 int ret;
242
243 memset(&fio, 0, sizeof(fio));
244 #ifdef __OpenBSD__
245 if (ioctl(fw->fd, SIOCGETFS, fiop) < 0)
246 #else
247 if (ioctl(fw->fd, SIOCGETFS, &fiop) < 0) /* XXX - darren! */
248 #endif
249 return (-1);
250
251 for (frp = fio.f_fout[(int)fio.f_active]; frp != NULL;
252 frp = fr.fr_next) {
253 if (fw_kcopy(fw, (u_char *)&fr, (u_long)frp, sizeof(fr)) < 0)
254 return (-1);
255 ipf_to_rule(&fr, &rule);
256 if ((ret = callback(&rule, arg)) != 0)
257 return (ret);
258 }
259 for (frp = fio.f_fin[(int)fio.f_active]; frp != NULL;
260 frp = fr.fr_next) {
261 if (fw_kcopy(fw, (u_char *)&fr, (u_long)frp, sizeof(fr)) < 0)
262 return (-1);
263 ipf_to_rule(&fr, &rule);
264 if ((ret = callback(&rule, arg)) != 0)
265 return (ret);
266 }
267 return (0);
268 }
269
270 fw_t *
fw_close(fw_t * fw)271 fw_close(fw_t *fw)
272 {
273 if (fw != NULL) {
274 if (fw->fd >= 0)
275 close(fw->fd);
276 if (fw->kfd >= 0)
277 close(fw->kfd);
278 free(fw);
279 }
280 return (NULL);
281 }
282