blob: 88a1ace3ac8652cb41348042c1f78401e63c2a6d [file] [log] [blame] [raw]
David Stes6c066772008-04-19 21:33:53 +00001
Darren Reed0fc5b372008-06-10 18:52:10 +00002/*
3 * Copyright (C) 1993-2001 by Darren Reed.
4 *
5 * See the IPFILTER.LICENCE file for details on licencing.
6 *
David Stes6c066772008-04-19 21:33:53 +00007 * $Id$
8 */
9
Darren Reedd4718fc2006-06-15 17:00:40 +000010#include <linux/version.h>
11#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
Darren Reedf96e37f2009-12-19 12:33:30 +000012# define __irq_h 1 /* stop it being included! */
Darren Reedd4718fc2006-06-15 17:00:40 +000013#else
14# define _LINUX_TCP_H
15#endif
16#include <net/ip.h>
17
Darren Reedda0443e2006-06-15 16:17:17 +000018#include "ipf-linux.h"
Darren Reedd4718fc2006-06-15 17:00:40 +000019
20#include <net/checksum.h>
21#include <net/route.h>
22
23#include <linux/random.h>
24#include <asm/ioctls.h>
Darren Reedda0443e2006-06-15 16:17:17 +000025
Darren Reedc6722242009-07-20 12:04:29 +000026#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
Darren Reedda0443e2006-06-15 16:17:17 +000027extern int sysctl_ip_default_ttl;
Darren Reedc6722242009-07-20 12:04:29 +000028#endif
Darren Reedda0443e2006-06-15 16:17:17 +000029
Darren Reedc6722242009-07-20 12:04:29 +000030extern ipf_main_softc_t ipfmain;
31
32static void ipf_timer_func(unsigned long);
Darren Reed278d16e2009-12-27 07:34:34 +000033static int ipf_send_ip __P((fr_info_t *, struct sk_buff *, struct sk_buff **));
Darren Reedda0443e2006-06-15 16:17:17 +000034
Darren Reedc4af1f32007-08-20 10:15:33 +000035ipfmutex_t ipl_mutex, ipf_auth_mx, ipf_rw, ipf_stinsert;
Darren Reedda0443e2006-06-15 16:17:17 +000036ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock;
David Stes6c066772008-04-19 21:33:53 +000037ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag, ipf_frcache, ipf_tokens;
Darren Reedc4af1f32007-08-20 10:15:33 +000038ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_authlk;
Darren Reedda0443e2006-06-15 16:17:17 +000039
Darren Reed0e5d9b82009-08-01 10:51:28 +000040#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
Darren Reedc6722242009-07-20 12:04:29 +000041extern int ip_finish_output(struct sk_buff *);
Darren Reed0e5d9b82009-08-01 10:51:28 +000042#endif
Darren Reedc6722242009-07-20 12:04:29 +000043
Darren Reed0e5d9b82009-08-01 10:51:28 +000044#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
Darren Reed278d16e2009-12-27 07:34:34 +000045static u_int ipf_linux_inout __P((u_int, struct sk_buff *, const struct net_device *, const struct net_device *, int (*okfn)(struct sk_buff *)));
Darren Reedc6722242009-07-20 12:04:29 +000046#else
Darren Reed278d16e2009-12-27 07:34:34 +000047static u_int ipf_linux_inout __P((u_int, struct sk_buff **, const struct net_device *, const struct net_device *, int (*okfn)(struct sk_buff *)));
Darren Reedc6722242009-07-20 12:04:29 +000048#endif
Darren Reedda0443e2006-06-15 16:17:17 +000049
Darren Reedd4718fc2006-06-15 17:00:40 +000050#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
51static struct nf_hook_ops ipf_hooks[] = {
52 {
53 .hook = ipf_linux_inout,
54 .owner = THIS_MODULE,
55 .pf = PF_INET,
Darren Reedc6722242009-07-20 12:04:29 +000056# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
57 .hooknum = NF_INET_PRE_ROUTING,
58# else
Darren Reedd4718fc2006-06-15 17:00:40 +000059 .hooknum = NF_IP_PRE_ROUTING,
Darren Reedc6722242009-07-20 12:04:29 +000060# endif
Darren Reedd4718fc2006-06-15 17:00:40 +000061 .priority = 200,
62 },
63 {
64 .hook = ipf_linux_inout,
65 .owner = THIS_MODULE,
66 .pf = PF_INET,
Darren Reedc6722242009-07-20 12:04:29 +000067# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
68 .hooknum = NF_INET_POST_ROUTING,
69# else
Darren Reedd4718fc2006-06-15 17:00:40 +000070 .hooknum = NF_IP_POST_ROUTING,
Darren Reedc6722242009-07-20 12:04:29 +000071# endif
Darren Reedd4718fc2006-06-15 17:00:40 +000072 .priority = 200,
73 },
74# ifdef USE_INET6
75 {
76 .hook = ipf_linux_inout,
77 .owner = THIS_MODULE,
78 .pf = PF_INET6,
Darren Reedc6722242009-07-20 12:04:29 +000079# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
80 .hooknum = NF_INET_PRE_ROUTING,
81# else
Darren Reedd4718fc2006-06-15 17:00:40 +000082 .hooknum = NF_IP_PRE_ROUTING,
Darren Reedc6722242009-07-20 12:04:29 +000083# endif
Darren Reedd4718fc2006-06-15 17:00:40 +000084 .priority = 200,
85 },
86 {
87 .hook = ipf_linux_inout,
88 .owner = THIS_MODULE,
89 .pf = PF_INET6,
Darren Reedc6722242009-07-20 12:04:29 +000090# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
91 .hooknum = NF_INET_POST_ROUTING,
92# else
Darren Reedd4718fc2006-06-15 17:00:40 +000093 .hooknum = NF_IP_POST_ROUTING,
Darren Reedc6722242009-07-20 12:04:29 +000094# endif
Darren Reedd4718fc2006-06-15 17:00:40 +000095 .priority = 200,
96 }
97# endif
98};
99#else
Darren Reedda0443e2006-06-15 16:17:17 +0000100static struct nf_hook_ops ipf_hooks[] = {
101 {
102 { NULL, NULL }, /* list */
103 ipf_linux_inout, /* hook */
104 PF_INET, /* pf */
105 NF_IP_PRE_ROUTING, /* hooknum */
106 200 /* priority */
107 },
108 {
109 { NULL, NULL}, /* list */
110 ipf_linux_inout, /* hook */
111 PF_INET, /* pf */
112 NF_IP_POST_ROUTING, /* hooknum */
113 200 /* priority */
114 },
Darren Reedd4718fc2006-06-15 17:00:40 +0000115# ifdef USE_INET6
Darren Reedda0443e2006-06-15 16:17:17 +0000116 {
117 { NULL, NULL }, /* list */
118 ipf_linux_inout, /* hook */
119 PF_INET6, /* pf */
120 NF_IP_PRE_ROUTING, /* hooknum */
121 200 /* priority */
122 },
123 {
124 { NULL, NULL}, /* list */
125 ipf_linux_inout, /* hook */
126 PF_INET6, /* pf */
127 NF_IP_POST_ROUTING, /* hooknum */
128 200 /* priority */
129 }
Darren Reedd4718fc2006-06-15 17:00:40 +0000130# endif
Darren Reedda0443e2006-06-15 16:17:17 +0000131};
Darren Reedd4718fc2006-06-15 17:00:40 +0000132#endif
Darren Reedda0443e2006-06-15 16:17:17 +0000133
134
135/*
136 * Filter ioctl interface.
137 */
Darren Reedc4af1f32007-08-20 10:15:33 +0000138int
139ipf_ioctl(struct inode *in, struct file *fp, u_int cmd, u_long arg)
Darren Reedda0443e2006-06-15 16:17:17 +0000140{
Darren Reedc4af1f32007-08-20 10:15:33 +0000141 int error = 0, unit = 0;
Darren Reedda0443e2006-06-15 16:17:17 +0000142 caddr_t data;
143 mode_t mode;
144
145 unit = MINOR(in->i_rdev);
146 if (unit < 0 || unit > IPL_LOGMAX)
147 return -ENXIO;
148
Darren Reedc6722242009-07-20 12:04:29 +0000149 if (ipfmain.ipf_running <= 0) {
Darren Reedda0443e2006-06-15 16:17:17 +0000150 if (unit != IPL_LOGIPF)
151 return -EIO;
152 if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET &&
Darren Reedd4718fc2006-06-15 17:00:40 +0000153 cmd != SIOCIPFSET && cmd != SIOCFRENB &&
Darren Reede29dd062009-03-20 06:46:57 +0000154 cmd != SIOCGETFS && cmd != SIOCGETFF &&
155 cmd != SIOCIPFINTERROR)
Darren Reedda0443e2006-06-15 16:17:17 +0000156 return -EIO;
157 }
158
159 mode = fp->f_mode;
160 data = (caddr_t)arg;
161
Darren Reedc6722242009-07-20 12:04:29 +0000162 error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, fp->f_uid, fp);
Darren Reedda0443e2006-06-15 16:17:17 +0000163 if (error != -1) {
164 SPL_X(s);
165 if (error > 0)
166 error = -error;
167 return error;
168 }
Darren Reedda0443e2006-06-15 16:17:17 +0000169 SPL_X(s);
Darren Reedc4af1f32007-08-20 10:15:33 +0000170
Darren Reedda0443e2006-06-15 16:17:17 +0000171 if (error > 0)
172 error = -error;
173 return error;
174}
175
176
Darren Reedc4af1f32007-08-20 10:15:33 +0000177u_32_t
178ipf_newisn(fr_info_t *fin)
Darren Reedda0443e2006-06-15 16:17:17 +0000179{
180 u_32_t isn;
181
Darren Reedc6722242009-07-20 12:04:29 +0000182#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
183 i6addr_t dst, src;
184
185 bcopy(&fin->fin_dst, &dst, sizeof(dst));
186 bcopy(&fin->fin_src, &src, sizeof(src));
187
188 isn = secure_tcpv6_sequence_number(&dst.in4.s_addr, &src.in4.s_addr,
189 fin->fin_dport, fin->fin_sport);
190#else
Darren Reedda0443e2006-06-15 16:17:17 +0000191 isn = secure_tcp_sequence_number(fin->fin_daddr, fin->fin_saddr,
192 fin->fin_dport, fin->fin_sport);
Darren Reedc6722242009-07-20 12:04:29 +0000193#endif
Darren Reedda0443e2006-06-15 16:17:17 +0000194 return isn;
195}
196
197
Darren Reedc4af1f32007-08-20 10:15:33 +0000198int
199ipf_send_reset(fr_info_t *fin)
Darren Reedda0443e2006-06-15 16:17:17 +0000200{
201 tcphdr_t *tcp, *tcp2;
202 int tlen, hlen;
203#ifdef USE_INET6
204 ip6_t *ip6;
205#endif
206 ip_t *ip;
207 mb_t *m;
208
209 tcp = fin->fin_dp;
210 if (tcp->th_flags & TH_RST)
211 return -1;
212
Darren Reedc4af1f32007-08-20 10:15:33 +0000213 if (ipf_checkl4sum(fin) == -1)
Darren Reedda0443e2006-06-15 16:17:17 +0000214 return -1;
Darren Reedc4af1f32007-08-20 10:15:33 +0000215
Darren Reedd4718fc2006-06-15 17:00:40 +0000216 m = skb_copy(fin->fin_m, GFP_ATOMIC);
217 if (m == NULL)
218 return -1;
Darren Reedda0443e2006-06-15 16:17:17 +0000219
220 tlen = (tcp->th_flags & (TH_SYN|TH_FIN)) ? 1 : 0;
221#ifdef USE_INET6
222 if (fin->fin_v == 6)
223 hlen = sizeof(ip6_t);
224 else
225#endif
226 hlen = sizeof(ip_t);
227 hlen += sizeof(*tcp2);
Darren Reedd4718fc2006-06-15 17:00:40 +0000228 skb_trim(m, hlen);
Darren Reedda0443e2006-06-15 16:17:17 +0000229
Darren Reedda0443e2006-06-15 16:17:17 +0000230 bzero(MTOD(m, char *), hlen);
Darren Reedd4718fc2006-06-15 17:00:40 +0000231 ip = MTOD(m, ip_t *);
232 bzero((char *)ip, hlen);
233 ip->ip_v = fin->fin_v;
234 tcp2 = (tcphdr_t *)((char *)ip + hlen - sizeof(*tcp2));
Darren Reedda0443e2006-06-15 16:17:17 +0000235 tcp2->th_dport = tcp->th_sport;
236 tcp2->th_sport = tcp->th_dport;
237 if (tcp->th_flags & TH_ACK) {
238 tcp2->th_seq = tcp->th_ack;
239 tcp2->th_flags = TH_RST;
240 } else {
241 tcp2->th_ack = ntohl(tcp->th_seq);
242 tcp2->th_ack += tlen;
243 tcp2->th_ack = htonl(tcp2->th_ack);
244 tcp2->th_flags = TH_RST|TH_ACK;
245 }
Darren Reedd4718fc2006-06-15 17:00:40 +0000246 tcp2->th_off = sizeof(*tcp2) >> 2;
Darren Reedda0443e2006-06-15 16:17:17 +0000247
Darren Reedda0443e2006-06-15 16:17:17 +0000248#ifdef USE_INET6
249 if (fin->fin_v == 6) {
Darren Reedd4718fc2006-06-15 17:00:40 +0000250 ip6 = (ip6_t *)ip;
Darren Reedda0443e2006-06-15 16:17:17 +0000251 ip6->ip6_src = fin->fin_dst6;
252 ip6->ip6_dst = fin->fin_src6;
253 ip6->ip6_plen = htons(sizeof(*tcp));
254 ip6->ip6_nxt = IPPROTO_TCP;
255 } else
256#endif
257 {
Darren Reedd4718fc2006-06-15 17:00:40 +0000258 ip->ip_hl = sizeof(*ip) >> 2;
Darren Reedda0443e2006-06-15 16:17:17 +0000259 ip->ip_src.s_addr = fin->fin_daddr;
260 ip->ip_dst.s_addr = fin->fin_saddr;
Darren Reedda0443e2006-06-15 16:17:17 +0000261 ip->ip_p = IPPROTO_TCP;
262 ip->ip_len = htons(sizeof(*ip) + sizeof(*tcp));
David Stes6c066772008-04-19 21:33:53 +0000263 tcp2->th_sum = fr_cksum(m, ip, IPPROTO_TCP, tcp2,
Darren Reedc4af1f32007-08-20 10:15:33 +0000264 ntohs(ip->ip_len));
Darren Reedda0443e2006-06-15 16:17:17 +0000265 }
Darren Reedc4af1f32007-08-20 10:15:33 +0000266 return ipf_send_ip(fin, m, &m);
Darren Reedda0443e2006-06-15 16:17:17 +0000267}
268
269
Darren Reedd4718fc2006-06-15 17:00:40 +0000270/*
271 * On input, ip_len is in network byte order
272 */
Darren Reedc4af1f32007-08-20 10:15:33 +0000273static int
274ipf_send_ip(fr_info_t *fin, struct sk_buff *sk, struct sk_buff **skp)
Darren Reedda0443e2006-06-15 16:17:17 +0000275{
Darren Reedd4718fc2006-06-15 17:00:40 +0000276 fr_info_t fnew;
Darren Reedda0443e2006-06-15 16:17:17 +0000277 ip_t *ip, *oip;
Darren Reedd4718fc2006-06-15 17:00:40 +0000278 int hlen;
Darren Reedda0443e2006-06-15 16:17:17 +0000279
280 ip = MTOD(sk, ip_t *);
Darren Reedd4718fc2006-06-15 17:00:40 +0000281 bzero((char *)&fnew, sizeof(fnew));
Darren Reedda0443e2006-06-15 16:17:17 +0000282 oip = fin->fin_ip;
283
Darren Reedda0443e2006-06-15 16:17:17 +0000284 switch (fin->fin_v)
285 {
286 case 4 :
Darren Reedd4718fc2006-06-15 17:00:40 +0000287 fnew.fin_v = 4;
Darren Reedda0443e2006-06-15 16:17:17 +0000288 ip->ip_hl = sizeof(*oip) >> 2;
289 ip->ip_tos = oip->ip_tos;
Darren Reedd4718fc2006-06-15 17:00:40 +0000290 ip->ip_id = 0;
Darren Reedc6722242009-07-20 12:04:29 +0000291#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
Darren Reedda0443e2006-06-15 16:17:17 +0000292 ip->ip_ttl = sysctl_ip_default_ttl;
Darren Reedc6722242009-07-20 12:04:29 +0000293#else
294 ip->ip_ttl = IPDEFTTL;
295#endif
Darren Reedda0443e2006-06-15 16:17:17 +0000296 ip->ip_sum = 0;
Darren Reedd4718fc2006-06-15 17:00:40 +0000297 ip->ip_off = 0x4000; /* IP_DF */
298 hlen = sizeof(*ip);
Darren Reedda0443e2006-06-15 16:17:17 +0000299 break;
300 default :
301 return EINVAL;
302 }
Darren Reedd4718fc2006-06-15 17:00:40 +0000303
304 fnew.fin_ifp = fin->fin_ifp;
305 fnew.fin_flx = FI_NOCKSUM;
306 fnew.fin_m = sk;
307 fnew.fin_ip = ip;
308 fnew.fin_mp = skp;
309 fnew.fin_hlen = hlen;
310 fnew.fin_dp = (char *)ip + hlen;
Darren Reedc4af1f32007-08-20 10:15:33 +0000311 (void) ipf_makefrip(hlen, ip, &fnew);
Darren Reed4a4c1062009-12-19 05:52:07 +0000312
Darren Reedc4af1f32007-08-20 10:15:33 +0000313 return ipf_fastroute(sk, skp, &fnew, NULL);
Darren Reedd4718fc2006-06-15 17:00:40 +0000314}
315
316
Darren Reedc4af1f32007-08-20 10:15:33 +0000317int
318ipf_send_icmp_err(int type, fr_info_t *fin, int isdst)
Darren Reedd4718fc2006-06-15 17:00:40 +0000319{
320 int hlen, code, leader, dlen;
321 struct net_device *ifp;
322 struct in_addr dst4;
323 struct icmp *icmp;
324 i6addr_t dst6;
325 u_short sz;
326#ifdef USE_INET6
327 ip6_t *ip6;
328 mb_t *mb;
329#endif
330 ip_t *ip;
331 mb_t *m, *m0;
332
333 if ((type < 0) || (type > ICMP_MAXTYPE))
334 return -1;
335
336 code = fin->fin_icode;
337
338#ifdef USE_INET6
339 if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int)))
340 return -1;
341#endif
342
Darren Reedc4af1f32007-08-20 10:15:33 +0000343 if (ipf_checkl4sum(fin) == -1)
Darren Reedd4718fc2006-06-15 17:00:40 +0000344 return -1;
Darren Reedd4718fc2006-06-15 17:00:40 +0000345
346 m0 = fin->fin_m;
347#ifdef USE_INET6
348 if (fin->fin_v == 6) {
349 sz = sizeof(ip6_t);
350 dlen = MIN(M_LEN(m0), 512);
351 sz += dlen;
352 hlen = sizeof(ip6_t);
353 type = icmptoicmp6types[type];
354 if (type == ICMP6_DST_UNREACH)
355 code = icmptoicmp6unreach[code];
356 } else
357#endif
358 {
359 if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT))
360 switch (ntohs(fin->fin_data[0]) >> 8)
361 {
362 case ICMP_ECHO :
363 case ICMP_TSTAMP :
364 case ICMP_IREQ :
365 case ICMP_MASKREQ :
366 break;
367 default :
368 return 0;
369 }
370
371 sz = sizeof(ip_t) * 2 + 4;
372 dlen = 8; /* 64 bits of data */
373 sz += dlen;
374 hlen = sizeof(ip_t);
375 }
376
377 leader = m0->data - m0->head;
378 if ((leader & 15) != 0)
379 leader += 16 - (leader & 15);
Darren Reedd42b2592008-08-09 13:44:13 +0000380 m = alloc_skb(sz + leader, GFP_ATOMIC);
381 if (m == NULL)
382 return -1;
Martti Kuparinenb4c6ea22007-10-25 09:26:49 +0000383
Darren Reedd42b2592008-08-09 13:44:13 +0000384 /* Set the data pointer */
385 skb_reserve(m, leader);
Darren Reedd4718fc2006-06-15 17:00:40 +0000386
387 bzero(MTOD(m, char *), (size_t)sz);
388
Darren Reedc6722242009-07-20 12:04:29 +0000389#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
390 skb_put(m, hlen);
391 skb_set_network_header(m,hlen);
392 ip = (ip_t *)ip_hdr(m);
393#else
Darren Reedd4718fc2006-06-15 17:00:40 +0000394 m->nh.iph = (struct iphdr *)skb_put(m, hlen);
395 ip = (ip_t *)m->nh.iph;
Darren Reedc6722242009-07-20 12:04:29 +0000396#endif
Darren Reedd4718fc2006-06-15 17:00:40 +0000397 ip->ip_v = fin->fin_v;
398
Darren Reedc6722242009-07-20 12:04:29 +0000399#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
400 skb_put(m, hlen + 4 + dlen);
401 skb_set_transport_header(m, hlen + 4 + dlen);
402 icmp = (icmphdr_t *)icmp_hdr(m);
403#else
Darren Reedd4718fc2006-06-15 17:00:40 +0000404 m->h.icmph = (struct icmphdr *)skb_put(m, hlen + 4 + dlen);
405 icmp = (icmphdr_t *)m->h.icmph;
Darren Reedc6722242009-07-20 12:04:29 +0000406#endif
Darren Reedd4718fc2006-06-15 17:00:40 +0000407 icmp->icmp_type = type & 0xff;
408 icmp->icmp_code = code & 0xff;
409#ifdef icmp_nextmtu
410 ifp = fin->fin_ifp;
Darren Reedc4af1f32007-08-20 10:15:33 +0000411 if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) {
412 if (fin->fin_mtu != 0) {
413 icmp->icmp_nextmtu = htons(fin->fin_mtu);
414
415 } else if (ifp->mtu != 0) {
416 icmp->icmp_nextmtu = htons(ifp->mtu);
417
418 } else {
419 icmp->icmp_nextmtu = htons(fin->fin_plen - 20);
420 }
421 }
Darren Reedd4718fc2006-06-15 17:00:40 +0000422#endif
423
424#ifdef USE_INET6
425 if (fin->fin_v == 6) {
426 int csz;
427
428 if (isdst == 0) {
Darren Reedea1bf372011-10-16 22:45:13 +0000429 if (ipf_ifpaddr(&ipfmain, 6, FRI_NORMAL, fin->fin_ifp,
Darren Reedc4af1f32007-08-20 10:15:33 +0000430 &dst6, NULL) == -1) {
Darren Reedd4718fc2006-06-15 17:00:40 +0000431 FREE_MB_T(m);
432 return -1;
433 }
434 } else
435 dst6 = fin->fin_dst6;
436
437 csz = sz;
438 sz -= sizeof(ip6_t);
439 ip6 = (ip6_t *)ip;
440 ip6->ip6_plen = htons((u_short)sz);
441 ip6->ip6_nxt = IPPROTO_ICMPV6;
442 ip6->ip6_src = dst6;
443 ip6->ip6_dst = fin->fin_src6;
444 sz -= offsetof(struct icmp, icmp_ip);
Darren Reedea1bf372011-10-16 22:45:13 +0000445 bcopy((char *)fin->fin_ip, (char *)&icmp->icmp_ip, sz);
Darren Reedd4718fc2006-06-15 17:00:40 +0000446 icmp->icmp_cksum = csz - sizeof(ip6_t);
447 } else
448#endif
449 {
450 ip = MTOD(m, ip_t *);
451 ip->ip_hl = sizeof(*ip) >> 2;
452 ip->ip_p = IPPROTO_ICMP;
453 ip->ip_len = (u_short)sz;
454 if (isdst == 0) {
Darren Reedc8a8b052008-08-14 09:18:44 +0000455 if (ipf_ifpaddr(&ipfmain, 4, FRI_NORMAL, fin->fin_ifp,
Darren Reedc4af1f32007-08-20 10:15:33 +0000456 &dst6, NULL) == -1) {
Darren Reedd4718fc2006-06-15 17:00:40 +0000457 FREE_MB_T(m);
458 return -1;
459 }
460 dst4 = dst6.in4;
461 } else
462 dst4 = fin->fin_dst;
463 ip->ip_src = dst4;
464 ip->ip_dst = fin->fin_src;
465 bcopy((char *)fin->fin_ip, (char *)&icmp->icmp_ip,
466 sizeof(*fin->fin_ip));
467 bcopy((char *)fin->fin_ip + fin->fin_hlen,
468 (char *)&icmp->icmp_ip + sizeof(*fin->fin_ip), 8);
469 icmp->icmp_cksum = ip_compute_csum((u_char *)icmp,
470 sz - sizeof(ip_t));
471 ip->ip_len = htons(sz);
472 }
473
474 /*
475 * Need to exit out of these so we don't recursively call rw_enter
476 * from fr_qout.
477 */
Darren Reedc4af1f32007-08-20 10:15:33 +0000478 return ipf_send_ip(fin, m, &m);
Darren Reedda0443e2006-06-15 16:17:17 +0000479}
480
481
Darren Reedc4af1f32007-08-20 10:15:33 +0000482int
483ipf_verifysrc(fr_info_t *fin)
Darren Reedda0443e2006-06-15 16:17:17 +0000484{
485 return 0;
486}
487
488
Darren Reedc4af1f32007-08-20 10:15:33 +0000489void
490ipf_checkv4sum(fr_info_t *fin)
Darren Reedda0443e2006-06-15 16:17:17 +0000491{
492 /*
493 * Linux 2.4.20-8smp (RedHat 9)
494 * Because ip_input() on linux clears the checksum flag in the sk_buff
495 * before calling the netfilter hook, it is not possible to take
496 * advantage of the work already done by the hardware.
497 */
498#ifdef IPFILTER_CKSUM
Darren Reedc4af1f32007-08-20 10:15:33 +0000499 if (ipf_checkl4sum(fin) == -1)
Darren Reedda0443e2006-06-15 16:17:17 +0000500 fin->fin_flx |= FI_BAD;
501#endif
502}
503
504
Darren Reedc4af1f32007-08-20 10:15:33 +0000505u_short
506ipf_nextipid(fr_info_t *fin)
Darren Reedda0443e2006-06-15 16:17:17 +0000507{
Darren Reedd4718fc2006-06-15 17:00:40 +0000508#if 1
509 static u_short ipid = 0;
510
511 return ipid++;
512#else
Darren Reedda0443e2006-06-15 16:17:17 +0000513 ip_t ip;
514
515 __ip_select_ident(&ip, NULL);
516 return ip.ip_id;
Darren Reedd4718fc2006-06-15 17:00:40 +0000517#endif
Darren Reedda0443e2006-06-15 16:17:17 +0000518}
519
520
Martti Kuparinenc07b1cc2008-10-20 12:35:24 +0000521/*
522 * xmin - pointer to mbuf where the IP packet starts
523 * mpp - pointer to the mbuf pointer that is the start of the mbuf chain
524 */
Darren Reedda0443e2006-06-15 16:17:17 +0000525/*ARGSUSED*/
Darren Reedc4af1f32007-08-20 10:15:33 +0000526int
Darren Reed197e04d2007-11-08 08:11:51 +0000527ipf_fastroute(mb_t *xmin, mb_t **mp, fr_info_t *fin, frdest_t *fdp)
Darren Reedda0443e2006-06-15 16:17:17 +0000528{
Darren Reedd4718fc2006-06-15 17:00:40 +0000529 struct net_device *ifp, *sifp;
530 struct in_addr dip;
531 struct rtable *rt;
Darren Reedbb421c42010-01-31 13:07:29 +0000532 frdest_t node;
Darren Reedd4718fc2006-06-15 17:00:40 +0000533 frentry_t *fr;
534 int err, sout;
535 ip_t *ip;
Darren Reedda0443e2006-06-15 16:17:17 +0000536
Darren Reedd4718fc2006-06-15 17:00:40 +0000537 rt = NULL;
538 fr = fin->fin_fr;
Darren Reed197e04d2007-11-08 08:11:51 +0000539 ip = MTOD(xmin, ip_t *);
Darren Reedd4718fc2006-06-15 17:00:40 +0000540 dip = ip->ip_dst;
541
Darren Reedbb421c42010-01-31 13:07:29 +0000542 if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) &&
543 (fdp->fd_type == FRD_DSTLIST)) {
544 if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0)
545 fdp = &node;
546 }
Darren Reedd4718fc2006-06-15 17:00:40 +0000547 if (fdp != NULL)
548 ifp = fdp->fd_ifp;
549 else
550 ifp = fin->fin_ifp;
551
552 if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) {
553 err = ENETUNREACH;
554 goto bad;
555 }
556
557 if ((fdp != NULL) && (fdp->fd_ip.s_addr))
558 dip = fdp->fd_ip;
559
560 switch (fin->fin_v)
561 {
562 case 4 :
563#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
564 err = ip_route_output(&rt, dip.s_addr, 0,
565 RT_TOS(ip->ip_tos) | RTO_CONN, 0);
566#else
567 err = 1;
568#endif
569 if (err != 0 || rt == NULL)
570 goto bad;
571
572 if (rt->u.dst.dev == NULL) {
573 err = EHOSTUNREACH;
574 goto bad;
575 }
576 break;
577 default :
578 err = EINVAL;
579 goto bad;
580 }
581
582 if (fin->fin_out == 0) {
583 sifp = fin->fin_ifp;
584 sout = fin->fin_out;
585 fin->fin_ifp = ifp;
586 fin->fin_out = 1;
Darren Reedc4af1f32007-08-20 10:15:33 +0000587 (void) ipf_acctpkt(fin, NULL);
Darren Reedd4718fc2006-06-15 17:00:40 +0000588 fin->fin_fr = NULL;
589 if (!fr || !(fr->fr_flags & FR_RETMASK)) {
590 u_32_t pass;
591
Darren Reed59e7fa82008-11-06 21:27:05 +0000592 (void) ipf_state_check(fin, &pass);
Darren Reedd4718fc2006-06-15 17:00:40 +0000593 }
594
Darren Reedc4af1f32007-08-20 10:15:33 +0000595 switch (ipf_nat_checkout(fin, NULL))
Darren Reedd4718fc2006-06-15 17:00:40 +0000596 {
597 case 0 :
598 break;
599 case 1 :
600 ip->ip_sum = 0;
601 break;
602 case -1 :
603 err = EINVAL;
604 goto bad;
605 break;
606 }
607
608 fin->fin_ifp = sifp;
609 fin->fin_out = sout;
610 }
611 ip->ip_sum = 0;
612
613
Darren Reed197e04d2007-11-08 08:11:51 +0000614 if (xmin->dst != NULL) {
615 dst_release(xmin->dst);
616 xmin->dst = NULL;
Darren Reedd4718fc2006-06-15 17:00:40 +0000617 }
618
Darren Reed197e04d2007-11-08 08:11:51 +0000619 xmin->dst = &rt->u.dst;
Darren Reedd4718fc2006-06-15 17:00:40 +0000620#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
Darren Reed197e04d2007-11-08 08:11:51 +0000621 if (xmin->len > xmin->dst->pmtu) {
Darren Reedd4718fc2006-06-15 17:00:40 +0000622 err = EMSGSIZE;
623 goto bad;
624 }
625#endif
626
627 switch (fin->fin_v)
628 {
629 case 4 :
630 ip->ip_sum = ip_fast_csum((u_char *)ip, ip->ip_hl);
631
Darren Reedc6722242009-07-20 12:04:29 +0000632 NF_HOOK(PF_INET,
633#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
634 NF_INET_LOCAL_OUT,
635#else
636 NF_IP_LOCAL_OUT,
637#endif
638 xmin, NULL, ifp, ip_finish_output);
Darren Reedd4718fc2006-06-15 17:00:40 +0000639 err = 0;
640 break;
641
642 default :
643 err = EINVAL;
644 break;
645 }
646
647 if (err == 0)
648 return 0;
649bad:
Darren Reed197e04d2007-11-08 08:11:51 +0000650 if (xmin != NULL)
651 kfree_skb(xmin);
Darren Reedd4718fc2006-06-15 17:00:40 +0000652 return err;
Darren Reedda0443e2006-06-15 16:17:17 +0000653}
654
655
Darren Reedc4af1f32007-08-20 10:15:33 +0000656int
Darren Reedc8a8b052008-08-14 09:18:44 +0000657ipf_ifpaddr(ipf_main_softc_t *softc, int v, int atype, void *ifptr,
658 i6addr_t *inp, i6addr_t *inpmask)
Darren Reedda0443e2006-06-15 16:17:17 +0000659{
660 struct sockaddr_in sin, sinmask;
Darren Reedda0443e2006-06-15 16:17:17 +0000661 struct net_device *dev;
Darren Reedd4718fc2006-06-15 17:00:40 +0000662 struct in_ifaddr *ifa;
Darren Reedda0443e2006-06-15 16:17:17 +0000663 struct in_device *ifp;
Darren Reedd4718fc2006-06-15 17:00:40 +0000664
665 if ((ifptr == NULL) || (ifptr == (void *)-1))
666 return -1;
Darren Reedda0443e2006-06-15 16:17:17 +0000667
668 dev = ifptr;
Darren Reedf5860e42009-07-31 20:08:23 +0000669#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
Darren Reedc6722242009-07-20 12:04:29 +0000670 ifp = __in_dev_get_rtnl(dev);
671#else
Darren Reedda0443e2006-06-15 16:17:17 +0000672 ifp = __in_dev_get(dev);
Darren Reedc6722242009-07-20 12:04:29 +0000673#endif
Darren Reedda0443e2006-06-15 16:17:17 +0000674
675 if (v == 4)
Darren Reedd4718fc2006-06-15 17:00:40 +0000676 inp->in4.s_addr = 0;
Darren Reedda0443e2006-06-15 16:17:17 +0000677#ifdef USE_INET6
678 else if (v == 6)
679 return -1;
680#endif
681
682 ifa = ifp->ifa_list;
683 while (ifa != NULL) {
684 if (ifa->ifa_flags & IFA_F_SECONDARY)
685 continue;
686 break;
687 }
688
689 if (ifa == NULL)
690 return -1;
691
692 sin.sin_family = AF_INET;
693 sinmask.sin_addr.s_addr = ifa->ifa_mask;
694 if (atype == FRI_BROADCAST)
695 sin.sin_addr.s_addr = ifa->ifa_broadcast;
696 else if (atype == FRI_PEERADDR)
697 sin.sin_addr.s_addr = ifa->ifa_address;
698 else
699 sin.sin_addr.s_addr = ifa->ifa_local;
700
Darren Reedc4af1f32007-08-20 10:15:33 +0000701 return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)&sin,
702 (struct sockaddr_in *)&sinmask,
703 &inp->in4, &inpmask->in4);
Darren Reedda0443e2006-06-15 16:17:17 +0000704}
705
706
Darren Reedc4af1f32007-08-20 10:15:33 +0000707void
708m_copydata(mb_t *m, int off, int len, caddr_t cp)
Darren Reedda0443e2006-06-15 16:17:17 +0000709{
710 bcopy(MTOD(m, char *) + off, cp, len);
711}
712
713
Darren Reedc6722242009-07-20 12:04:29 +0000714int ipfattach(ipf_main_softc_t *softc)
Darren Reedda0443e2006-06-15 16:17:17 +0000715{
716 int err, i;
717
718 SPL_NET(s);
Darren Reedc6722242009-07-20 12:04:29 +0000719 if (softc->ipf_running > 0) {
Darren Reedda0443e2006-06-15 16:17:17 +0000720 SPL_X(s);
721 return -EBUSY;
722 }
723
Darren Reedc6722242009-07-20 12:04:29 +0000724 MUTEX_INIT(&softc->ipf_rw, "ipf rw mutex");
725 MUTEX_INIT(&softc->ipf_timeoutlock, "ipf timeout lock mutex");
726 RWLOCK_INIT(&softc->ipf_tokens, "ipf token rwlock");
Darren Reedda0443e2006-06-15 16:17:17 +0000727
728 for (i = 0; i < sizeof(ipf_hooks)/sizeof(ipf_hooks[0]); i++) {
729 err = nf_register_hook(&ipf_hooks[i]);
730 if (err != 0)
731 return err;
732 }
733
Darren Reedc6722242009-07-20 12:04:29 +0000734 if (ipf_init_all(softc) < 0) {
Darren Reedda0443e2006-06-15 16:17:17 +0000735 for (i = 0; i < sizeof(ipf_hooks)/sizeof(ipf_hooks[0]); i++)
736 nf_unregister_hook(&ipf_hooks[i]);
737 SPL_X(s);
Darren Reedc6722242009-07-20 12:04:29 +0000738 return -EIO;
Darren Reedda0443e2006-06-15 16:17:17 +0000739 }
740
Darren Reedda0443e2006-06-15 16:17:17 +0000741#ifdef notyet
Darren Reedc4af1f32007-08-20 10:15:33 +0000742 if (ipf_control_forwarding & 1)
Darren Reedda0443e2006-06-15 16:17:17 +0000743 ipv4_devconf.forwarding = 1;
744#endif
745
746 SPL_X(s);
David Stes6c066772008-04-19 21:33:53 +0000747
Darren Reedc6722242009-07-20 12:04:29 +0000748 init_timer(&softc->ipf_timer);
Darren Reedf5860e42009-07-31 20:08:23 +0000749#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
Darren Reedc6722242009-07-20 12:04:29 +0000750 softc->ipf_timer.function = ipf_timer_func;
751 softc->ipf_timer.data = (unsigned long)softc;
752#else
753 softc->ipf_timer.function = ipf_timer_func;
754 softc->ipf_timer.data = softc;
David Stes6c066772008-04-19 21:33:53 +0000755#endif
Darren Reedc6722242009-07-20 12:04:29 +0000756 softc->ipf_timer.expires = (HZ / IPF_HZ_DIVIDE) * IPF_HZ_MULT;
757 add_timer(&softc->ipf_timer);
758 mod_timer(&softc->ipf_timer, HZ/2 + jiffies);
David Stes6c066772008-04-19 21:33:53 +0000759
Darren Reedda0443e2006-06-15 16:17:17 +0000760 return 0;
761}
762
Darren Reedc6722242009-07-20 12:04:29 +0000763int ipfdetach(ipf_main_softc_t *softc)
Darren Reedda0443e2006-06-15 16:17:17 +0000764{
Darren Reedd42b2592008-08-09 13:44:13 +0000765 int i;
Darren Reedda0443e2006-06-15 16:17:17 +0000766
Darren Reedc6722242009-07-20 12:04:29 +0000767 del_timer(&softc->ipf_timer);
Darren Reedda0443e2006-06-15 16:17:17 +0000768
Darren Reedd42b2592008-08-09 13:44:13 +0000769 SPL_NET(s);
David Stes6c066772008-04-19 21:33:53 +0000770
Darren Reedd42b2592008-08-09 13:44:13 +0000771 for (i = 0; i < sizeof(ipf_hooks)/sizeof(ipf_hooks[0]); i++)
772 nf_unregister_hook(&ipf_hooks[i]);
Darren Reedda0443e2006-06-15 16:17:17 +0000773
774#ifdef notyet
Darren Reedc4af1f32007-08-20 10:15:33 +0000775 if (ipf_control_forwarding & 2)
Darren Reedda0443e2006-06-15 16:17:17 +0000776 ipv4_devconf.forwarding = 0;
777#endif
778
Darren Reedc6722242009-07-20 12:04:29 +0000779 ipf_fini_all(softc);
Darren Reedda0443e2006-06-15 16:17:17 +0000780
Darren Reedc6722242009-07-20 12:04:29 +0000781 MUTEX_DESTROY(&softc->ipf_timeoutlock);
782 MUTEX_DESTROY(&softc->ipf_rw);
783 RW_DESTROY(&softc->ipf_tokens);
Darren Reedda0443e2006-06-15 16:17:17 +0000784
785 SPL_X(s);
786
787 return 0;
788}
789
790
Darren Reedc4af1f32007-08-20 10:15:33 +0000791static u_int
792ipf_linux_inout(hooknum, skbp, inifp, outifp, okfn)
793 u_int hooknum;
Darren Reedc6722242009-07-20 12:04:29 +0000794#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
795 struct sk_buff *skbp;
796#else
Darren Reedc4af1f32007-08-20 10:15:33 +0000797 struct sk_buff **skbp;
Darren Reedc6722242009-07-20 12:04:29 +0000798#endif
Darren Reedc4af1f32007-08-20 10:15:33 +0000799 const struct net_device *inifp, *outifp;
800 int (*okfn)(struct sk_buff *);
Darren Reedda0443e2006-06-15 16:17:17 +0000801{
802 int result, hlen, dir;
803 void *ifp;
804 ip_t *ip;
805 mb_t *sk;
806
807 if (inifp == NULL && outifp != NULL) {
808 dir = IPF_OUT;
809 ifp = (void *)outifp;
810 } else if (inifp != NULL && outifp == NULL) {
811 dir = IPF_IN;
812 ifp = (void *)inifp;
813 } else
814 return NF_DROP;
815
Darren Reedc6722242009-07-20 12:04:29 +0000816#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
817 sk = skbp;
818#else
Darren Reedda0443e2006-06-15 16:17:17 +0000819 sk = *skbp;
Darren Reedc6722242009-07-20 12:04:29 +0000820#endif
Darren Reedda0443e2006-06-15 16:17:17 +0000821 ip = MTOD(sk, ip_t *);
822 if (ip->ip_v == 4) {
823 hlen = ip->ip_hl << 2;
Darren Reedda0443e2006-06-15 16:17:17 +0000824#ifdef USE_INET6
825 } else if (ip->ip_v == 6) {
826 hlen = sizeof(ip6_t);
827#endif
828 } else
829 return NF_DROP;
Darren Reedc6722242009-07-20 12:04:29 +0000830 result = ipf_check(&ipfmain, ip, hlen, (struct net_device *)ifp, dir, &sk);
831#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
832 *skbp = sk;
833#endif
Darren Reedda0443e2006-06-15 16:17:17 +0000834 /*
835 * This is kind of not always right...*skbp == NULL might really be
836 * a drop but Linux expects *skbp != NULL for NF_DROP.
837 */
Darren Reedc6722242009-07-20 12:04:29 +0000838 if (sk == NULL)
Darren Reedda0443e2006-06-15 16:17:17 +0000839 return NF_STOLEN;
840
841 if (result != 0)
842 return NF_DROP;
Darren Reedd4718fc2006-06-15 17:00:40 +0000843
Darren Reedda0443e2006-06-15 16:17:17 +0000844 return NF_ACCEPT;
845}
846
847
Darren Reedc4af1f32007-08-20 10:15:33 +0000848INLINE void
849ipf_read_enter(ipfrwlock_t *rwlk)
Darren Reedda0443e2006-06-15 16:17:17 +0000850{
Darren Reedd4718fc2006-06-15 17:00:40 +0000851#if defined(IPFDEBUG) && !defined(_KERNEL)
Darren Reedda0443e2006-06-15 16:17:17 +0000852 if (rwlk->ipf_magic != 0x97dd8b3a) {
853 printk("ipf_read_enter:rwlk %p ipf_magic 0x%x\n",
854 rwlk, rwlk->ipf_magic);
855 rwlk->ipf_magic = 0;
856 *((int *)rwlk->ipf_magic) = 1;
857 }
858#endif
Darren Reedc6722242009-07-20 12:04:29 +0000859 read_lock_bh(&rwlk->ipf_lk);
Darren Reedda0443e2006-06-15 16:17:17 +0000860 ATOMIC_INC32(rwlk->ipf_isr);
861}
862
863
Darren Reedc4af1f32007-08-20 10:15:33 +0000864INLINE void
865ipf_write_enter(ipfrwlock_t *rwlk)
Darren Reedda0443e2006-06-15 16:17:17 +0000866{
Darren Reedd4718fc2006-06-15 17:00:40 +0000867#if defined(IPFDEBUG) && !defined(_KERNEL)
Darren Reedda0443e2006-06-15 16:17:17 +0000868 if (rwlk->ipf_magic != 0x97dd8b3a) {
869 printk("ipf_write_enter:rwlk %p ipf_magic 0x%x\n",
870 rwlk, rwlk->ipf_magic);
871 rwlk->ipf_magic = 0;
872 *((int *)rwlk->ipf_magic) = 1;
873 }
874#endif
Darren Reedc6722242009-07-20 12:04:29 +0000875 write_lock_bh(&rwlk->ipf_lk);
Darren Reedda0443e2006-06-15 16:17:17 +0000876 rwlk->ipf_isw = 1;
877}
878
879
Darren Reedc4af1f32007-08-20 10:15:33 +0000880INLINE void
881ipf_rw_exit(ipfrwlock_t *rwlk)
Darren Reedda0443e2006-06-15 16:17:17 +0000882{
Darren Reedd4718fc2006-06-15 17:00:40 +0000883#if defined(IPFDEBUG) && !defined(_KERNEL)
Darren Reedda0443e2006-06-15 16:17:17 +0000884 if (rwlk->ipf_magic != 0x97dd8b3a) {
885 printk("ipf_rw_exit:rwlk %p ipf_magic 0x%x\n",
886 rwlk, rwlk->ipf_magic);
887 rwlk->ipf_magic = 0;
888 *((int *)rwlk->ipf_magic) = 1;
889 }
890#endif
891 if (rwlk->ipf_isw > 0) {
892 rwlk->ipf_isw = 0;
Darren Reedc6722242009-07-20 12:04:29 +0000893 write_unlock_bh(&rwlk->ipf_lk);
Darren Reedda0443e2006-06-15 16:17:17 +0000894 } else if (rwlk->ipf_isr > 0) {
895 ATOMIC_DEC32(rwlk->ipf_isr);
Darren Reedc6722242009-07-20 12:04:29 +0000896 read_unlock_bh(&rwlk->ipf_lk);
Darren Reedda0443e2006-06-15 16:17:17 +0000897 } else {
898 panic("rwlk->ipf_isw %d isr %d rwlk %p name [%s]\n",
899 rwlk->ipf_isw, rwlk->ipf_isr, rwlk, rwlk->ipf_lname);
900 }
901}
902
903
904/*
905 * This is not a perfect solution for a downgrade because we can lose the lock
906 * on the object of desire.
907 */
Darren Reedc4af1f32007-08-20 10:15:33 +0000908INLINE void
909ipf_rw_downgrade(ipfrwlock_t *rwlk)
Darren Reedda0443e2006-06-15 16:17:17 +0000910{
911 ipf_rw_exit(rwlk);
Darren Reedd4718fc2006-06-15 17:00:40 +0000912 ipf_read_enter(rwlk);
913}
914
David Stes6c066772008-04-19 21:33:53 +0000915void ipf_rw_init(rwlck, name)
916ipfrwlock_t *rwlck;
917char *name;
918{
Darren Reedd42b2592008-08-09 13:44:13 +0000919 memset(rwlck, 0, sizeof(*rwlck));
920 rwlck->ipf_lname = name;
921 rwlock_init(&rwlck->ipf_lk);
David Stes6c066772008-04-19 21:33:53 +0000922}
Darren Reedd4718fc2006-06-15 17:00:40 +0000923
924#if 0
925void dumpskbuff(sk)
Martti Kuparinen74b6c5c2007-10-25 12:55:40 +0000926 struct sk_buff *sk;
Darren Reedd4718fc2006-06-15 17:00:40 +0000927{
928 char line[80], *t;
929 u_char *s1, *s2;
930 int len, i;
931 u_char c;
932
933 while (sk != NULL) {
934 len = sk->end - sk->data;
935
936 for (s1 = MTOD(sk, char *); len > 0; ) {
937 t = line;
938 s2 = s1;
939 for (i = 0; i < len && i < 16; i++) {
940 c = *s2++;
941 sprintf(t, "%02x", c);
942 t += 2;
943 if (i &1)
944 *t++ = ' ';
945 }
946 *t++ = ' ';
947 *t++ = ' ';
948 for (i = 0; i < len && i < 16; i++) {
949 c = *s1++;
950 *t++ = (c >= 32 && c < 127) ? c : '.';
951 }
952 *t = '\0';
953 printf("%p@%03d %s\n", sk, s1 - MTOD(sk, u_char *), line);
954 if (len > 16) {
955 len -= 16;
956 s1 += 16;
957 } else
958 len = 0;
959 }
960 sk = sk->next;
961 }
962}
963#endif
964
965
Darren Reedc4af1f32007-08-20 10:15:33 +0000966mb_t *
967m_pullup(mb_t *m, int len)
Darren Reedd4718fc2006-06-15 17:00:40 +0000968{
969 if (len <= M_LEN(m))
970 return m;
971 kfree_skb(m);
972 return NULL;
973}
974
975
976/* ------------------------------------------------------------------------ */
Darren Reedc4af1f32007-08-20 10:15:33 +0000977/* Function: ipf_pullup */
Darren Reedd4718fc2006-06-15 17:00:40 +0000978/* Returns: NULL == pullup failed, else pointer to protocol header */
Darren Reed197e04d2007-11-08 08:11:51 +0000979/* Parameters: xmin(I)- pointer to buffer where data packet starts */
Darren Reedd4718fc2006-06-15 17:00:40 +0000980/* fin(I) - pointer to packet information */
981/* len(I) - number of bytes to pullup */
982/* */
983/* Attempt to move at least len bytes (from the start of the buffer) into a */
984/* single buffer for ease of access. Operating system native functions are */
985/* used to manage buffers - if necessary. If the entire packet ends up in */
Darren Reedc4af1f32007-08-20 10:15:33 +0000986/* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */
Darren Reedd4718fc2006-06-15 17:00:40 +0000987/* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */
988/* and ONLY if the pullup succeeds. */
989/* */
990/* We assume that 'min' is a pointer to a buffer that is part of the chain */
991/* of buffers that starts at *fin->fin_mp. */
992/* ------------------------------------------------------------------------ */
Darren Reedc4af1f32007-08-20 10:15:33 +0000993void *
Darren Reed197e04d2007-11-08 08:11:51 +0000994ipf_pullup(mb_t *xmin, fr_info_t *fin, int len)
Darren Reedd4718fc2006-06-15 17:00:40 +0000995{
Darren Reedc6722242009-07-20 12:04:29 +0000996 int dpoff, ipoff;
Darren Reed197e04d2007-11-08 08:11:51 +0000997 mb_t *m = xmin;
Darren Reedd4718fc2006-06-15 17:00:40 +0000998 char *ip;
999
1000 if (m == NULL)
1001 return NULL;
1002
1003 ip = (char *)fin->fin_ip;
1004 if ((fin->fin_flx & FI_COALESCE) != 0)
1005 return ip;
1006
1007 ipoff = fin->fin_ipoff;
1008 if (fin->fin_dp != NULL)
1009 dpoff = (char *)fin->fin_dp - (char *)ip;
1010 else
1011 dpoff = 0;
1012
1013 if (M_LEN(m) < len) {
1014 m = m_pullup(m, len);
1015 *fin->fin_mp = m;
1016 fin->fin_m = m;
1017 if (m == NULL) {
Darren Reedd4718fc2006-06-15 17:00:40 +00001018 return NULL;
1019 }
1020 ip = MTOD(m, char *) + ipoff;
1021 }
1022
Darren Reedd4718fc2006-06-15 17:00:40 +00001023 fin->fin_ip = (ip_t *)ip;
1024 if (fin->fin_dp != NULL)
1025 fin->fin_dp = (char *)fin->fin_ip + dpoff;
1026
1027 if (len == fin->fin_plen)
1028 fin->fin_flx |= FI_COALESCE;
1029 return ip;
Darren Reedda0443e2006-06-15 16:17:17 +00001030}
Darren Reedc4af1f32007-08-20 10:15:33 +00001031
1032
Darren Reedc6722242009-07-20 12:04:29 +00001033#ifndef ipf_random
Darren Reedc4af1f32007-08-20 10:15:33 +00001034/*
1035 * In the face of no kernel random function, this is implemented...it is
1036 * not meant to be random, just a fill in.
1037 */
1038int
Darren Reedc8a8b052008-08-14 09:18:44 +00001039ipf_random()
Darren Reedc4af1f32007-08-20 10:15:33 +00001040{
1041 static int last = 0;
1042 static int calls = 0;
1043 struct timeval tv;
1044 int number;
1045
1046 GETKTIME(&tv);
1047 last *= tv.tv_usec + calls++;
Darren Reedc6722242009-07-20 12:04:29 +00001048 last += (int)&range * ipfmain.ipf_ticks;
Darren Reedc4af1f32007-08-20 10:15:33 +00001049 number = last + tv.tv_sec;
Darren Reedc4af1f32007-08-20 10:15:33 +00001050 return number;
1051}
David Stes6c066772008-04-19 21:33:53 +00001052#endif
Darren Reedc6722242009-07-20 12:04:29 +00001053
David Stes6c066772008-04-19 21:33:53 +00001054
1055int ipf_inject(fin, m)
1056fr_info_t *fin;
1057mb_t *m;
1058{
Darren Reedd42b2592008-08-09 13:44:13 +00001059 FREE_MB_T(m);
David Stes6c066772008-04-19 21:33:53 +00001060
Darren Reedd42b2592008-08-09 13:44:13 +00001061 fin->fin_m = NULL;
1062 fin->fin_ip = NULL;
David Stes6c066772008-04-19 21:33:53 +00001063
Darren Reedd42b2592008-08-09 13:44:13 +00001064 return EINVAL;
David Stes6c066772008-04-19 21:33:53 +00001065}
1066
Darren Reedc6722242009-07-20 12:04:29 +00001067
Darren Reed95108882009-07-22 01:43:16 +00001068/*
1069 * Copy data from a buffer back into the indicated mbuf chain,
1070 * starting "off" bytes from the beginning, extending the mbuf
1071 * chain if necessary.
1072 */
1073void
1074m_copyback(m0, off0, len0, cp)
1075 mb_t *m0;
1076 int off0;
1077 int len0;
1078 caddr_t cp;
1079{
1080 mb_t *m = m0, *n;
1081 int totlen = 0;
1082 int mlen;
1083 int off = off0;
1084 int len = len0;
1085
1086 if (m0 == 0)
1087 return;
1088 while (off > (mlen = m->len)) {
1089 off -= mlen;
1090 totlen += mlen;
1091 if (m->next == 0) {
1092 n = alloc_skb(off, in_interrupt() ? GFP_ATOMIC :
1093 GFP_KERNEL);
1094 if (n == 0)
1095 return;
1096 n->len = off;
1097 m->next = n;
1098 }
1099 m = m->next;
1100 }
1101 while (len > 0) {
1102 mlen = MIN(m->len - off, len);
1103 bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
1104 cp += mlen;
1105 len -= mlen;
1106 mlen += off;
1107 off = 0;
1108 totlen += mlen;
1109 if (len == 0)
1110 break;
1111 if (m->next == 0) {
1112 n = alloc_skb(len, in_interrupt() ? GFP_ATOMIC :
1113 GFP_KERNEL);
1114 if (n == 0)
1115 return;
1116 n->len = len;
1117 m->next = n;
1118 }
1119 m = m->next;
1120 }
1121}
1122
1123
Darren Reedc6722242009-07-20 12:04:29 +00001124static void
1125ipf_timer_func(unsigned long value)
1126{
1127 ipf_main_softc_t *softc = (ipf_main_softc_t *)value;
1128
1129 READ_ENTER(&softc->ipf_global);
1130
1131 if (softc->ipf_running > 0)
1132 ipf_slowtimer(softc);
1133
1134 if (softc->ipf_running == -1 || softc->ipf_running == 1)
1135 mod_timer(&softc->ipf_timer, HZ/2 + jiffies);
1136
1137 RWLOCK_EXIT(&softc->ipf_global);
1138}
Darren Reedb59f3662011-10-16 22:03:19 +00001139
1140
1141#ifdef USE_INET6
1142INLINE void
1143ipf_checkv6sum(fin)
1144 fr_info_t *fin;
1145{
1146# ifdef IPFILTER_CKSUM
1147 if (fr_checkl4sum(fin) == -1)
1148 fin->fin_flx |= FI_BAD;
1149# endif
1150}
1151#endif