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