ip_fw_log.c revision 238277
1/*-
2 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_log.c 238277 2012-07-09 07:16:19Z hrs $");
28
29/*
30 * Logging support for ipfw
31 */
32
33#include "opt_ipfw.h"
34#include "opt_inet.h"
35#ifndef INET
36#error IPFIREWALL requires INET.
37#endif /* INET */
38#include "opt_inet6.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/mbuf.h>
43#include <sys/kernel.h>
44#include <sys/socket.h>
45#include <sys/sysctl.h>
46#include <sys/syslog.h>
47#include <sys/lock.h>
48#include <sys/rwlock.h>
49#include <net/ethernet.h> /* for ETHERTYPE_IP */
50#include <net/if.h>
51#include <net/if_clone.h>
52#include <net/vnet.h>
53#include <net/if_types.h>	/* for IFT_ETHER */
54#include <net/bpf.h>		/* for BPF */
55
56#include <netinet/in.h>
57#include <netinet/ip.h>
58#include <netinet/ip_icmp.h>
59#include <netinet/ip_var.h>
60#include <netinet/ip_fw.h>
61#include <netinet/ipfw/ip_fw_private.h>
62#include <netinet/tcp_var.h>
63#include <netinet/udp.h>
64
65#include <netinet/ip6.h>
66#include <netinet/icmp6.h>
67#ifdef INET6
68#include <netinet6/in6_var.h>	/* ip6_sprintf() */
69#endif
70
71#ifdef MAC
72#include <security/mac/mac_framework.h>
73#endif
74
75/*
76 * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T
77 * Other macros just cast void * into the appropriate type
78 */
79#define	L3HDR(T, ip)	((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
80#define	TCP(p)		((struct tcphdr *)(p))
81#define	SCTP(p)		((struct sctphdr *)(p))
82#define	UDP(p)		((struct udphdr *)(p))
83#define	ICMP(p)		((struct icmphdr *)(p))
84#define	ICMP6(p)	((struct icmp6_hdr *)(p))
85
86#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
87#define SNP(buf) buf, sizeof(buf)
88
89#ifdef WITHOUT_BPF
90void
91ipfw_log_bpf(int onoff)
92{
93}
94#else /* !WITHOUT_BPF */
95static struct ifnet *log_if;	/* hook to attach to bpf */
96static struct rwlock log_if_lock;
97#define	LOGIF_LOCK_INIT(x)	rw_init(&log_if_lock, "ipfw log_if lock")
98#define	LOGIF_LOCK_DESTROY(x)	rw_destroy(&log_if_lock)
99#define	LOGIF_RLOCK(x)		rw_rlock(&log_if_lock)
100#define	LOGIF_RUNLOCK(x)	rw_runlock(&log_if_lock)
101#define	LOGIF_WLOCK(x)		rw_wlock(&log_if_lock)
102#define	LOGIF_WUNLOCK(x)	rw_wunlock(&log_if_lock)
103
104#define	IPFWNAME	"ipfw"
105
106/* we use this dummy function for all ifnet callbacks */
107static int
108log_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr)
109{
110	return EINVAL;
111}
112
113static int
114ipfw_log_output(struct ifnet *ifp, struct mbuf *m,
115	struct sockaddr *dst, struct route *ro)
116{
117	if (m != NULL)
118		m_freem(m);
119	return EINVAL;
120}
121
122static void
123ipfw_log_start(struct ifnet* ifp)
124{
125	panic("ipfw_log_start() must not be called");
126}
127
128static const u_char ipfwbroadcastaddr[6] =
129	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
130
131static int
132ipfw_log_clone_match(struct if_clone *ifc, const char *name)
133{
134
135	return (strncmp(name, IPFWNAME, sizeof(IPFWNAME) - 1) == 0);
136}
137
138static int
139ipfw_log_clone_create(struct if_clone *ifc, char *name, size_t len,
140    caddr_t params)
141{
142	int error;
143	int unit;
144	struct ifnet *ifp;
145
146	error = ifc_name2unit(name, &unit);
147	if (error)
148		return (error);
149
150	error = ifc_alloc_unit(ifc, &unit);
151	if (error)
152		return (error);
153
154	ifp = if_alloc(IFT_ETHER);
155	if (ifp == NULL) {
156		ifc_free_unit(ifc, unit);
157		return (ENOSPC);
158	}
159	ifp->if_dname = IPFWNAME;
160	ifp->if_dunit = unit;
161	snprintf(ifp->if_xname, IFNAMSIZ, "%s%d", IPFWNAME, unit);
162	strlcpy(name, ifp->if_xname, len);
163	ifp->if_mtu = 65536;
164	ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
165	ifp->if_init = (void *)log_dummy;
166	ifp->if_ioctl = log_dummy;
167	ifp->if_start = ipfw_log_start;
168	ifp->if_output = ipfw_log_output;
169	ifp->if_addrlen = 6;
170	ifp->if_hdrlen = 14;
171	ifp->if_broadcastaddr = ipfwbroadcastaddr;
172	ifp->if_baudrate = IF_Mbps(10);
173
174	LOGIF_WLOCK();
175	if (log_if == NULL)
176		log_if = ifp;
177	else {
178		LOGIF_WUNLOCK();
179		if_free(ifp);
180		ifc_free_unit(ifc, unit);
181		return (EEXIST);
182	}
183	LOGIF_WUNLOCK();
184	if_attach(ifp);
185	bpfattach(ifp, DLT_EN10MB, 14);
186
187	return (0);
188}
189
190static int
191ipfw_log_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
192{
193	int unit;
194
195	if (ifp == NULL)
196		return (0);
197
198	LOGIF_WLOCK();
199	if (log_if != NULL && ifp == log_if)
200		log_if = NULL;
201	else {
202		LOGIF_WUNLOCK();
203		return (EINVAL);
204	}
205	LOGIF_WUNLOCK();
206
207	unit = ifp->if_dunit;
208	bpfdetach(ifp);
209	if_detach(ifp);
210	if_free(ifp);
211	ifc_free_unit(ifc, unit);
212
213	return (0);
214}
215
216static struct if_clone ipfw_log_cloner = IFC_CLONE_INITIALIZER(
217    IPFWNAME, NULL, IF_MAXUNIT,
218    NULL, ipfw_log_clone_match, ipfw_log_clone_create, ipfw_log_clone_destroy);
219
220void
221ipfw_log_bpf(int onoff)
222{
223
224	if (onoff) {
225		LOGIF_LOCK_INIT();
226		if_clone_attach(&ipfw_log_cloner);
227	} else {
228		if_clone_detach(&ipfw_log_cloner);
229		LOGIF_LOCK_DESTROY();
230	}
231}
232#endif /* !WITHOUT_BPF */
233
234/*
235 * We enter here when we have a rule with O_LOG.
236 * XXX this function alone takes about 2Kbytes of code!
237 */
238void
239ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
240    struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
241    struct ip *ip)
242{
243	char *action;
244	int limit_reached = 0;
245	char action2[92], proto[128], fragment[32];
246
247	if (V_fw_verbose == 0) {
248#ifndef WITHOUT_BPF
249		LOGIF_RLOCK();
250		if (log_if == NULL || log_if->if_bpf == NULL) {
251			LOGIF_RUNLOCK();
252			return;
253		}
254
255		if (args->eh) /* layer2, use orig hdr */
256			BPF_MTAP2(log_if, args->eh, ETHER_HDR_LEN, m);
257		else
258			/* Add fake header. Later we will store
259			 * more info in the header.
260			 */
261			BPF_MTAP2(log_if, "DDDDDDSSSSSS\x08\x00", ETHER_HDR_LEN, m);
262		LOGIF_RUNLOCK();
263#endif /* !WITHOUT_BPF */
264		return;
265	}
266	/* the old 'log' function */
267	fragment[0] = '\0';
268	proto[0] = '\0';
269
270	if (f == NULL) {	/* bogus pkt */
271		if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit)
272			return;
273		V_norule_counter++;
274		if (V_norule_counter == V_verbose_limit)
275			limit_reached = V_verbose_limit;
276		action = "Refuse";
277	} else {	/* O_LOG is the first action, find the real one */
278		ipfw_insn *cmd = ACTION_PTR(f);
279		ipfw_insn_log *l = (ipfw_insn_log *)cmd;
280
281		if (l->max_log != 0 && l->log_left == 0)
282			return;
283		l->log_left--;
284		if (l->log_left == 0)
285			limit_reached = l->max_log;
286		cmd += F_LEN(cmd);	/* point to first action */
287		if (cmd->opcode == O_ALTQ) {
288			ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd;
289
290			snprintf(SNPARGS(action2, 0), "Altq %d",
291				altq->qid);
292			cmd += F_LEN(cmd);
293		}
294		if (cmd->opcode == O_PROB)
295			cmd += F_LEN(cmd);
296
297		if (cmd->opcode == O_TAG)
298			cmd += F_LEN(cmd);
299
300		action = action2;
301		switch (cmd->opcode) {
302		case O_DENY:
303			action = "Deny";
304			break;
305
306		case O_REJECT:
307			if (cmd->arg1==ICMP_REJECT_RST)
308				action = "Reset";
309			else if (cmd->arg1==ICMP_UNREACH_HOST)
310				action = "Reject";
311			else
312				snprintf(SNPARGS(action2, 0), "Unreach %d",
313					cmd->arg1);
314			break;
315
316		case O_UNREACH6:
317			if (cmd->arg1==ICMP6_UNREACH_RST)
318				action = "Reset";
319			else
320				snprintf(SNPARGS(action2, 0), "Unreach %d",
321					cmd->arg1);
322			break;
323
324		case O_ACCEPT:
325			action = "Accept";
326			break;
327		case O_COUNT:
328			action = "Count";
329			break;
330		case O_DIVERT:
331			snprintf(SNPARGS(action2, 0), "Divert %d",
332				cmd->arg1);
333			break;
334		case O_TEE:
335			snprintf(SNPARGS(action2, 0), "Tee %d",
336				cmd->arg1);
337			break;
338		case O_SETFIB:
339			snprintf(SNPARGS(action2, 0), "SetFib %d",
340				cmd->arg1);
341			break;
342		case O_SKIPTO:
343			snprintf(SNPARGS(action2, 0), "SkipTo %d",
344				cmd->arg1);
345			break;
346		case O_PIPE:
347			snprintf(SNPARGS(action2, 0), "Pipe %d",
348				cmd->arg1);
349			break;
350		case O_QUEUE:
351			snprintf(SNPARGS(action2, 0), "Queue %d",
352				cmd->arg1);
353			break;
354		case O_FORWARD_IP: {
355			ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;
356			int len;
357			struct in_addr dummyaddr;
358			if (sa->sa.sin_addr.s_addr == INADDR_ANY)
359				dummyaddr.s_addr = htonl(tablearg);
360			else
361				dummyaddr.s_addr = sa->sa.sin_addr.s_addr;
362
363			len = snprintf(SNPARGS(action2, 0), "Forward to %s",
364				inet_ntoa(dummyaddr));
365
366			if (sa->sa.sin_port)
367				snprintf(SNPARGS(action2, len), ":%d",
368				    sa->sa.sin_port);
369			}
370			break;
371#ifdef INET6
372		case O_FORWARD_IP6: {
373			char buf[INET6_ADDRSTRLEN];
374			ipfw_insn_sa6 *sa = (ipfw_insn_sa6 *)cmd;
375			int len;
376
377			len = snprintf(SNPARGS(action2, 0), "Forward to [%s]",
378			    ip6_sprintf(buf, &sa->sa.sin6_addr));
379
380			if (sa->sa.sin6_port)
381				snprintf(SNPARGS(action2, len), ":%u",
382				    sa->sa.sin6_port);
383			}
384			break;
385#endif
386		case O_NETGRAPH:
387			snprintf(SNPARGS(action2, 0), "Netgraph %d",
388				cmd->arg1);
389			break;
390		case O_NGTEE:
391			snprintf(SNPARGS(action2, 0), "Ngtee %d",
392				cmd->arg1);
393			break;
394		case O_NAT:
395			action = "Nat";
396 			break;
397		case O_REASS:
398			action = "Reass";
399			break;
400		case O_CALLRETURN:
401			if (cmd->len & F_NOT)
402				action = "Return";
403			else
404				snprintf(SNPARGS(action2, 0), "Call %d",
405				    cmd->arg1);
406			break;
407		default:
408			action = "UNKNOWN";
409			break;
410		}
411	}
412
413	if (hlen == 0) {	/* non-ip */
414		snprintf(SNPARGS(proto, 0), "MAC");
415
416	} else {
417		int len;
418#ifdef INET6
419		char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2];
420#else
421		char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
422#endif
423		struct icmphdr *icmp;
424		struct tcphdr *tcp;
425		struct udphdr *udp;
426#ifdef INET6
427		struct ip6_hdr *ip6 = NULL;
428		struct icmp6_hdr *icmp6;
429		u_short ip6f_mf;
430#endif
431		src[0] = '\0';
432		dst[0] = '\0';
433#ifdef INET6
434		ip6f_mf = offset & IP6F_MORE_FRAG;
435		offset &= IP6F_OFF_MASK;
436
437		if (IS_IP6_FLOW_ID(&(args->f_id))) {
438			char ip6buf[INET6_ADDRSTRLEN];
439			snprintf(src, sizeof(src), "[%s]",
440			    ip6_sprintf(ip6buf, &args->f_id.src_ip6));
441			snprintf(dst, sizeof(dst), "[%s]",
442			    ip6_sprintf(ip6buf, &args->f_id.dst_ip6));
443
444			ip6 = (struct ip6_hdr *)ip;
445			tcp = (struct tcphdr *)(((char *)ip) + hlen);
446			udp = (struct udphdr *)(((char *)ip) + hlen);
447		} else
448#endif
449		{
450			tcp = L3HDR(struct tcphdr, ip);
451			udp = L3HDR(struct udphdr, ip);
452
453			inet_ntoa_r(ip->ip_src, src);
454			inet_ntoa_r(ip->ip_dst, dst);
455		}
456
457		switch (args->f_id.proto) {
458		case IPPROTO_TCP:
459			len = snprintf(SNPARGS(proto, 0), "TCP %s", src);
460			if (offset == 0)
461				snprintf(SNPARGS(proto, len), ":%d %s:%d",
462				    ntohs(tcp->th_sport),
463				    dst,
464				    ntohs(tcp->th_dport));
465			else
466				snprintf(SNPARGS(proto, len), " %s", dst);
467			break;
468
469		case IPPROTO_UDP:
470			len = snprintf(SNPARGS(proto, 0), "UDP %s", src);
471			if (offset == 0)
472				snprintf(SNPARGS(proto, len), ":%d %s:%d",
473				    ntohs(udp->uh_sport),
474				    dst,
475				    ntohs(udp->uh_dport));
476			else
477				snprintf(SNPARGS(proto, len), " %s", dst);
478			break;
479
480		case IPPROTO_ICMP:
481			icmp = L3HDR(struct icmphdr, ip);
482			if (offset == 0)
483				len = snprintf(SNPARGS(proto, 0),
484				    "ICMP:%u.%u ",
485				    icmp->icmp_type, icmp->icmp_code);
486			else
487				len = snprintf(SNPARGS(proto, 0), "ICMP ");
488			len += snprintf(SNPARGS(proto, len), "%s", src);
489			snprintf(SNPARGS(proto, len), " %s", dst);
490			break;
491#ifdef INET6
492		case IPPROTO_ICMPV6:
493			icmp6 = (struct icmp6_hdr *)(((char *)ip) + hlen);
494			if (offset == 0)
495				len = snprintf(SNPARGS(proto, 0),
496				    "ICMPv6:%u.%u ",
497				    icmp6->icmp6_type, icmp6->icmp6_code);
498			else
499				len = snprintf(SNPARGS(proto, 0), "ICMPv6 ");
500			len += snprintf(SNPARGS(proto, len), "%s", src);
501			snprintf(SNPARGS(proto, len), " %s", dst);
502			break;
503#endif
504		default:
505			len = snprintf(SNPARGS(proto, 0), "P:%d %s",
506			    args->f_id.proto, src);
507			snprintf(SNPARGS(proto, len), " %s", dst);
508			break;
509		}
510
511#ifdef INET6
512		if (IS_IP6_FLOW_ID(&(args->f_id))) {
513			if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
514				snprintf(SNPARGS(fragment, 0),
515				    " (frag %08x:%d@%d%s)",
516				    args->f_id.extra,
517				    ntohs(ip6->ip6_plen) - hlen,
518				    ntohs(offset) << 3, ip6f_mf ? "+" : "");
519		} else
520#endif
521		{
522			int ipoff, iplen;
523			ipoff = ntohs(ip->ip_off);
524			iplen = ntohs(ip->ip_len);
525			if (ipoff & (IP_MF | IP_OFFMASK))
526				snprintf(SNPARGS(fragment, 0),
527				    " (frag %d:%d@%d%s)",
528				    ntohs(ip->ip_id), iplen - (ip->ip_hl << 2),
529				    offset << 3,
530				    (ipoff & IP_MF) ? "+" : "");
531		}
532	}
533#ifdef __FreeBSD__
534	if (oif || m->m_pkthdr.rcvif)
535		log(LOG_SECURITY | LOG_INFO,
536		    "ipfw: %d %s %s %s via %s%s\n",
537		    f ? f->rulenum : -1,
538		    action, proto, oif ? "out" : "in",
539		    oif ? oif->if_xname : m->m_pkthdr.rcvif->if_xname,
540		    fragment);
541	else
542#endif
543		log(LOG_SECURITY | LOG_INFO,
544		    "ipfw: %d %s %s [no if info]%s\n",
545		    f ? f->rulenum : -1,
546		    action, proto, fragment);
547	if (limit_reached)
548		log(LOG_SECURITY | LOG_NOTICE,
549		    "ipfw: limit %d reached on entry %d\n",
550		    limit_reached, f ? f->rulenum : -1);
551}
552/* end of file */
553