1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31/*
32 * Logging support for ipfw
33 */
34
35#include "opt_ipfw.h"
36#include "opt_inet.h"
37#ifndef INET
38#error IPFIREWALL requires INET.
39#endif /* INET */
40#include "opt_inet6.h"
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/kernel.h>
45#include <sys/mbuf.h>
46#include <sys/socket.h>
47#include <sys/sysctl.h>
48#include <sys/syslog.h>
49#include <net/ethernet.h> /* for ETHERTYPE_IP */
50#include <net/if.h>
51#include <net/if_var.h>
52#include <net/vnet.h>
53
54#include <netinet/in.h>
55#include <netinet/ip.h>
56#include <netinet/ip_icmp.h>
57#include <netinet/ip_var.h>
58#include <netinet/ip_fw.h>
59#include <netinet/tcp_var.h>
60#include <netinet/udp.h>
61
62#include <netinet/ip6.h>
63#include <netinet/icmp6.h>
64#ifdef INET6
65#include <netinet6/in6_var.h>	/* ip6_sprintf() */
66#endif
67
68#include <netpfil/ipfw/ip_fw_private.h>
69
70#ifdef MAC
71#include <security/mac/mac_framework.h>
72#endif
73
74/*
75 * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T
76 * Other macros just cast void * into the appropriate type
77 */
78#define	L3HDR(T, ip)	((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
79#define	TCP(p)		((struct tcphdr *)(p))
80#define	SCTP(p)		((struct sctphdr *)(p))
81#define	UDP(p)		((struct udphdr *)(p))
82#define	ICMP(p)		((struct icmphdr *)(p))
83#define	ICMP6(p)	((struct icmp6_hdr *)(p))
84
85#ifdef __APPLE__
86#undef snprintf
87#define snprintf	sprintf
88#define SNPARGS(buf, len) buf + len
89#define SNP(buf) buf
90#else	/* !__APPLE__ */
91#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
92#define SNP(buf) buf, sizeof(buf)
93#endif /* !__APPLE__ */
94
95#define	TARG(k, f)	IP_FW_ARG_TABLEARG(chain, k, f)
96/*
97 * We enter here when we have a rule with O_LOG.
98 * XXX this function alone takes about 2Kbytes of code!
99 */
100void
101ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
102    struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip)
103{
104	char *action;
105	int limit_reached = 0;
106	char action2[92], proto[128], fragment[32];
107
108	if (V_fw_verbose == 0) {
109		if (args->flags & IPFW_ARGS_LENMASK)
110			ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags));
111		else if (args->flags & IPFW_ARGS_ETHER)
112			/* layer2, use orig hdr */
113			ipfw_bpf_mtap(args->m);
114		else {
115			/* Add fake header. Later we will store
116			 * more info in the header.
117			 */
118			if (ip->ip_v == 4)
119				ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00",
120				    ETHER_HDR_LEN, args->m);
121			else if (ip->ip_v == 6)
122				ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd",
123				    ETHER_HDR_LEN, args->m);
124			else
125				/* Obviously bogus EtherType. */
126				ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff",
127				    ETHER_HDR_LEN, args->m);
128		}
129		return;
130	}
131	/* the old 'log' function */
132	fragment[0] = '\0';
133	proto[0] = '\0';
134
135	if (f == NULL) {	/* bogus pkt */
136		if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit)
137			return;
138		V_norule_counter++;
139		if (V_norule_counter == V_verbose_limit)
140			limit_reached = V_verbose_limit;
141		action = "Refuse";
142	} else {	/* O_LOG is the first action, find the real one */
143		ipfw_insn *cmd = ACTION_PTR(f);
144		ipfw_insn_log *l = (ipfw_insn_log *)cmd;
145
146		if (l->max_log != 0 && l->log_left == 0)
147			return;
148		l->log_left--;
149		if (l->log_left == 0)
150			limit_reached = l->max_log;
151		cmd += F_LEN(cmd);	/* point to first action */
152		if (cmd->opcode == O_ALTQ) {
153			ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd;
154
155			snprintf(SNPARGS(action2, 0), "Altq %d",
156				altq->qid);
157			cmd += F_LEN(cmd);
158		}
159		if (cmd->opcode == O_PROB || cmd->opcode == O_TAG ||
160		    cmd->opcode == O_SETDSCP)
161			cmd += F_LEN(cmd);
162
163		action = action2;
164		switch (cmd->opcode) {
165		case O_DENY:
166			action = "Deny";
167			break;
168
169		case O_REJECT:
170			if (cmd->arg1==ICMP_REJECT_RST)
171				action = "Reset";
172			else if (cmd->arg1==ICMP_REJECT_ABORT)
173				action = "Abort";
174			else if (cmd->arg1==ICMP_UNREACH_HOST)
175				action = "Reject";
176			else
177				snprintf(SNPARGS(action2, 0), "Unreach %d",
178					cmd->arg1);
179			break;
180
181		case O_UNREACH6:
182			if (cmd->arg1==ICMP6_UNREACH_RST)
183				action = "Reset";
184			else if (cmd->arg1==ICMP6_UNREACH_ABORT)
185				action = "Abort";
186			else
187				snprintf(SNPARGS(action2, 0), "Unreach %d",
188					cmd->arg1);
189			break;
190
191		case O_ACCEPT:
192			action = "Accept";
193			break;
194		case O_COUNT:
195			action = "Count";
196			break;
197		case O_DIVERT:
198			snprintf(SNPARGS(action2, 0), "Divert %d",
199				TARG(cmd->arg1, divert));
200			break;
201		case O_TEE:
202			snprintf(SNPARGS(action2, 0), "Tee %d",
203				TARG(cmd->arg1, divert));
204			break;
205		case O_SETFIB:
206			snprintf(SNPARGS(action2, 0), "SetFib %d",
207				TARG(cmd->arg1, fib) & 0x7FFF);
208			break;
209		case O_SKIPTO:
210			snprintf(SNPARGS(action2, 0), "SkipTo %d",
211				TARG(cmd->arg1, skipto));
212			break;
213		case O_PIPE:
214			snprintf(SNPARGS(action2, 0), "Pipe %d",
215				TARG(cmd->arg1, pipe));
216			break;
217		case O_QUEUE:
218			snprintf(SNPARGS(action2, 0), "Queue %d",
219				TARG(cmd->arg1, pipe));
220			break;
221		case O_FORWARD_IP: {
222			char buf[INET_ADDRSTRLEN];
223			ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;
224			int len;
225			struct in_addr dummyaddr;
226			if (sa->sa.sin_addr.s_addr == INADDR_ANY)
227				dummyaddr.s_addr = htonl(tablearg);
228			else
229				dummyaddr.s_addr = sa->sa.sin_addr.s_addr;
230
231			len = snprintf(SNPARGS(action2, 0), "Forward to %s",
232				inet_ntoa_r(dummyaddr, buf));
233
234			if (sa->sa.sin_port)
235				snprintf(SNPARGS(action2, len), ":%d",
236				    sa->sa.sin_port);
237			}
238			break;
239#ifdef INET6
240		case O_FORWARD_IP6: {
241			char buf[INET6_ADDRSTRLEN];
242			ipfw_insn_sa6 *sa = (ipfw_insn_sa6 *)cmd;
243			int len;
244
245			len = snprintf(SNPARGS(action2, 0), "Forward to [%s]",
246			    ip6_sprintf(buf, &sa->sa.sin6_addr));
247
248			if (sa->sa.sin6_port)
249				snprintf(SNPARGS(action2, len), ":%u",
250				    sa->sa.sin6_port);
251			}
252			break;
253#endif
254		case O_NETGRAPH:
255			snprintf(SNPARGS(action2, 0), "Netgraph %d",
256				cmd->arg1);
257			break;
258		case O_NGTEE:
259			snprintf(SNPARGS(action2, 0), "Ngtee %d",
260				cmd->arg1);
261			break;
262		case O_NAT:
263			action = "Nat";
264 			break;
265		case O_REASS:
266			action = "Reass";
267			break;
268		case O_CALLRETURN:
269			if (cmd->len & F_NOT)
270				action = "Return";
271			else
272				snprintf(SNPARGS(action2, 0), "Call %d",
273				    cmd->arg1);
274			break;
275		case O_EXTERNAL_ACTION:
276			snprintf(SNPARGS(action2, 0), "Eaction %s",
277			    ((struct named_object *)SRV_OBJECT(chain,
278			    cmd->arg1))->name);
279			break;
280		default:
281			action = "UNKNOWN";
282			break;
283		}
284	}
285
286	if (hlen == 0) {	/* non-ip */
287		snprintf(SNPARGS(proto, 0), "MAC");
288
289	} else {
290		int len;
291#ifdef INET6
292		char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2];
293#else
294		char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
295#endif
296		struct icmphdr *icmp;
297		struct tcphdr *tcp;
298		struct udphdr *udp;
299#ifdef INET6
300		struct ip6_hdr *ip6 = NULL;
301		struct icmp6_hdr *icmp6;
302		u_short ip6f_mf;
303#endif
304		src[0] = '\0';
305		dst[0] = '\0';
306#ifdef INET6
307		ip6f_mf = offset & IP6F_MORE_FRAG;
308		offset &= IP6F_OFF_MASK;
309
310		if (IS_IP6_FLOW_ID(&(args->f_id))) {
311			char ip6buf[INET6_ADDRSTRLEN];
312			snprintf(src, sizeof(src), "[%s]",
313			    ip6_sprintf(ip6buf, &args->f_id.src_ip6));
314			snprintf(dst, sizeof(dst), "[%s]",
315			    ip6_sprintf(ip6buf, &args->f_id.dst_ip6));
316
317			ip6 = (struct ip6_hdr *)ip;
318			tcp = (struct tcphdr *)(((char *)ip) + hlen);
319			udp = (struct udphdr *)(((char *)ip) + hlen);
320		} else
321#endif
322		{
323			tcp = L3HDR(struct tcphdr, ip);
324			udp = L3HDR(struct udphdr, ip);
325
326			inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
327			inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
328		}
329
330		switch (args->f_id.proto) {
331		case IPPROTO_TCP:
332			len = snprintf(SNPARGS(proto, 0), "TCP %s", src);
333			if (offset == 0)
334				snprintf(SNPARGS(proto, len), ":%d %s:%d",
335				    ntohs(tcp->th_sport),
336				    dst,
337				    ntohs(tcp->th_dport));
338			else
339				snprintf(SNPARGS(proto, len), " %s", dst);
340			break;
341
342		case IPPROTO_UDP:
343		case IPPROTO_UDPLITE:
344			len = snprintf(SNPARGS(proto, 0), "UDP%s%s",
345			    args->f_id.proto == IPPROTO_UDP ? " ": "Lite ",
346			    src);
347			if (offset == 0)
348				snprintf(SNPARGS(proto, len), ":%d %s:%d",
349				    ntohs(udp->uh_sport),
350				    dst,
351				    ntohs(udp->uh_dport));
352			else
353				snprintf(SNPARGS(proto, len), " %s", dst);
354			break;
355
356		case IPPROTO_ICMP:
357			icmp = L3HDR(struct icmphdr, ip);
358			if (offset == 0)
359				len = snprintf(SNPARGS(proto, 0),
360				    "ICMP:%u.%u ",
361				    icmp->icmp_type, icmp->icmp_code);
362			else
363				len = snprintf(SNPARGS(proto, 0), "ICMP ");
364			len += snprintf(SNPARGS(proto, len), "%s", src);
365			snprintf(SNPARGS(proto, len), " %s", dst);
366			break;
367#ifdef INET6
368		case IPPROTO_ICMPV6:
369			icmp6 = (struct icmp6_hdr *)(((char *)ip) + hlen);
370			if (offset == 0)
371				len = snprintf(SNPARGS(proto, 0),
372				    "ICMPv6:%u.%u ",
373				    icmp6->icmp6_type, icmp6->icmp6_code);
374			else
375				len = snprintf(SNPARGS(proto, 0), "ICMPv6 ");
376			len += snprintf(SNPARGS(proto, len), "%s", src);
377			snprintf(SNPARGS(proto, len), " %s", dst);
378			break;
379#endif
380		default:
381			len = snprintf(SNPARGS(proto, 0), "P:%d %s",
382			    args->f_id.proto, src);
383			snprintf(SNPARGS(proto, len), " %s", dst);
384			break;
385		}
386
387#ifdef INET6
388		if (IS_IP6_FLOW_ID(&(args->f_id))) {
389			if (offset || ip6f_mf)
390				snprintf(SNPARGS(fragment, 0),
391				    " (frag %08x:%d@%d%s)",
392				    args->f_id.extra,
393				    ntohs(ip6->ip6_plen) - hlen,
394				    ntohs(offset) << 3, ip6f_mf ? "+" : "");
395		} else
396#endif
397		{
398			int ipoff, iplen;
399			ipoff = ntohs(ip->ip_off);
400			iplen = ntohs(ip->ip_len);
401			if (ipoff & (IP_MF | IP_OFFMASK))
402				snprintf(SNPARGS(fragment, 0),
403				    " (frag %d:%d@%d%s)",
404				    ntohs(ip->ip_id), iplen - (ip->ip_hl << 2),
405				    offset << 3,
406				    (ipoff & IP_MF) ? "+" : "");
407		}
408	}
409#ifdef __FreeBSD__
410	log(LOG_SECURITY | LOG_INFO, "ipfw: %d %s %s %s via %s%s\n",
411	    f ? f->rulenum : -1, action, proto,
412	    args->flags & IPFW_ARGS_OUT ? "out" : "in", args->ifp->if_xname,
413	    fragment);
414#else
415	log(LOG_SECURITY | LOG_INFO, "ipfw: %d %s %s [no if info]%s\n",
416	    f ? f->rulenum : -1, action, proto, fragment);
417#endif
418	if (limit_reached)
419		log(LOG_SECURITY | LOG_NOTICE,
420		    "ipfw: limit %d reached on entry %d\n",
421		    limit_reached, f ? f->rulenum : -1);
422}
423/* end of file */
424